Merge pull request #11953 from freqtrade/drop_3.10

Drop support for python 3.10
This commit is contained in:
Matthias
2025-07-06 08:58:54 +02:00
committed by GitHub
81 changed files with 403 additions and 428 deletions
+3 -3
View File
@@ -25,7 +25,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ "ubuntu-22.04", "ubuntu-24.04" ] 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: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -160,7 +160,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ "macos-14", "macos-15" ] os: [ "macos-14", "macos-15" ]
python-version: ["3.10", "3.11", "3.12", "3.13"] python-version: ["3.11", "3.12", "3.13"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -288,7 +288,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ windows-latest ] os: [ windows-latest ]
python-version: ["3.10", "3.11", "3.12", "3.13"] python-version: ["3.11", "3.12", "3.13"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
+2 -2
View File
@@ -64,7 +64,7 @@ Please find the complete documentation on the [freqtrade website](https://www.fr
## Features ## 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] **Persistence**: Persistence is achieved through sqlite.
- [x] **Dry-run**: Run the bot without paying money. - [x] **Dry-run**: Run the bot without paying money.
- [x] **Backtesting**: Run a simulation of your buy/sell strategy. - [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 ### 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/) - [pip](https://pip.pypa.io/en/stable/installing/)
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [TA-Lib](https://ta-lib.github.io/ta-lib-python/) - [TA-Lib](https://ta-lib.github.io/ta-lib-python/)
+1 -1
View File
@@ -87,7 +87,7 @@ To run this bot we recommend you a linux cloud instance with a minimum of:
Alternatively Alternatively
- Python 3.10+ - Python 3.11+
- pip (pip3) - pip (pip3)
- git - git
- TA-Lib - TA-Lib
+4 -4
View File
@@ -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). 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 !!! 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<yourversion>-dev` / `python<yourversion>-devel`) must be available for the installation to complete successfully. Also, python headers (`python<yourversion>-dev` / `python<yourversion>-devel`) must be available for the installation to complete successfully.
!!! Warning "Up-to-date clock" !!! Warning "Up-to-date clock"
@@ -42,7 +42,7 @@ These requirements apply to both [Script Installation](#script-installation) and
### Install guide ### 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/) * [pip](https://pip.pypa.io/en/stable/installing/)
* [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [virtualenv](https://virtualenv.pypa.io/en/stable/installation.html) (Recommended) * [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. OS Specific steps are listed first, the common section below is necessary for all systems.
!!! Note !!! 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" === "Debian/Ubuntu"
#### Install necessary dependencies #### Install necessary dependencies
@@ -179,7 +179,7 @@ You can as well update, configure and reset the codebase of your bot with `./scr
** --install ** ** --install **
With this option, the script will install the bot and most dependencies: 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` * Mandatory software as: `ta-lib`
* Setup your virtualenv under `.venv/` * Setup your virtualenv under `.venv/`
+2 -2
View File
@@ -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. 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. 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 ## 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). 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. 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. Other versions must be downloaded from the above link.
+1 -1
View File
@@ -3,7 +3,7 @@
__main__.py for Freqtrade __main__.py for Freqtrade
To launch Freqtrade as a module To launch Freqtrade as a module
> python -m freqtrade (with Python >= 3.10) > python -m freqtrade (with Python >= 3.11)
""" """
from freqtrade import main from freqtrade import main
+4 -9
View File
@@ -4,9 +4,8 @@ This module contains the argument manager class
import logging import logging
import re import re
from datetime import datetime, timezone from datetime import UTC, datetime
from typing import Self
from typing_extensions import Self
from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.exceptions import ConfigurationError from freqtrade.exceptions import ConfigurationError
@@ -151,9 +150,7 @@ class TimeRange:
starts = rvals[index] starts = rvals[index]
if stype[0] == "date" and len(starts) == 8: if stype[0] == "date" and len(starts) == 8:
start = int( start = int(
datetime.strptime(starts, "%Y%m%d") datetime.strptime(starts, "%Y%m%d").replace(tzinfo=UTC).timestamp()
.replace(tzinfo=timezone.utc)
.timestamp()
) )
elif len(starts) == 13: elif len(starts) == 13:
start = int(starts) // 1000 start = int(starts) // 1000
@@ -164,9 +161,7 @@ class TimeRange:
stops = rvals[index] stops = rvals[index]
if stype[1] == "date" and len(stops) == 8: if stype[1] == "date" and len(stops) == 8:
stop = int( stop = int(
datetime.strptime(stops, "%Y%m%d") datetime.strptime(stops, "%Y%m%d").replace(tzinfo=UTC).timestamp()
.replace(tzinfo=timezone.utc)
.timestamp()
) )
elif len(stops) == 13: elif len(stops) == 13:
stop = int(stops) // 1000 stop = int(stops) // 1000
+2 -2
View File
@@ -5,7 +5,7 @@ Helpers when analyzing backtest data
import logging import logging
import zipfile import zipfile
from copy import copy from copy import copy
from datetime import datetime, timezone from datetime import UTC, datetime
from io import BytesIO, StringIO from io import BytesIO, StringIO
from pathlib import Path from pathlib import Path
from typing import Any, Literal from typing import Any, Literal
@@ -324,7 +324,7 @@ def find_existing_backtest_stats(
if min_backtest_date is not None: if min_backtest_date is not None:
backtest_date = strategy_metadata["backtest_start_time"] 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: if backtest_date < min_backtest_date:
# Do not use a cached result for this strategy as first result is too old. # Do not use a cached result for this strategy as first result is too old.
del run_ids[strategy_name] del run_ids[strategy_name]
+8 -8
View File
@@ -7,7 +7,7 @@ Common Interface for bot and strategy to access data.
import logging import logging
from collections import deque from collections import deque
from datetime import datetime, timezone from datetime import UTC, datetime
from typing import Any from typing import Any
from pandas import DataFrame, Timedelta, Timestamp, to_timedelta 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!) :param candle_type: Any of the enum CandleType (must match trading mode!)
""" """
pair_key = (pair, timeframe, candle_type) 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 # 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"): def _set_producer_pairs(self, pairlist: list[str], producer_name: str = "default"):
@@ -131,7 +131,7 @@ class DataProvider:
"data": { "data": {
"key": pair_key, "key": pair_key,
"df": dataframe.tail(1), "df": dataframe.tail(1),
"la": datetime.now(timezone.utc), "la": datetime.now(UTC),
}, },
} }
self.__rpc.send_msg(msg) self.__rpc.send_msg(msg)
@@ -164,7 +164,7 @@ class DataProvider:
if producer_name not in self.__producer_pairs_df: if producer_name not in self.__producer_pairs_df:
self.__producer_pairs_df[producer_name] = {} 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) self.__producer_pairs_df[producer_name][pair_key] = (dataframe, _last_analyzed)
logger.debug(f"External DataFrame for {pair_key} from {producer_name} added.") 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 we have no data from this Producer yet
if producer_name not in self.__producer_pairs_df: if producer_name not in self.__producer_pairs_df:
# We don't have this data yet, return empty DataFrame and datetime (01-01-1970) # 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 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]: 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) # 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 # We have it, return this data
df, la = self.__producer_pairs_df[producer_name][pair_key] 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: if (max_index := self.__slice_index.get(pair)) is not None:
df = df.iloc[max(0, max_index - MAX_DATAFRAME_CANDLES) : max_index] df = df.iloc[max(0, max_index - MAX_DATAFRAME_CANDLES) : max_index]
else: else:
return (DataFrame(), datetime.fromtimestamp(0, tz=timezone.utc)) return (DataFrame(), datetime.fromtimestamp(0, tz=UTC))
return df, date return df, date
else: else:
return (DataFrame(), datetime.fromtimestamp(0, tz=timezone.utc)) return (DataFrame(), datetime.fromtimestamp(0, tz=UTC))
@property @property
def runmode(self) -> RunMode: def runmode(self) -> RunMode:
@@ -8,7 +8,7 @@ import logging
import re import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from pandas import DataFrame, to_datetime from pandas import DataFrame, to_datetime
@@ -118,8 +118,8 @@ class IDataHandler(ABC):
df = self._ohlcv_load(pair, timeframe, None, candle_type) df = self._ohlcv_load(pair, timeframe, None, candle_type)
if df.empty: if df.empty:
return ( return (
datetime.fromtimestamp(0, tz=timezone.utc), datetime.fromtimestamp(0, tz=UTC),
datetime.fromtimestamp(0, tz=timezone.utc), datetime.fromtimestamp(0, tz=UTC),
0, 0,
) )
return df.iloc[0]["date"].to_pydatetime(), df.iloc[-1]["date"].to_pydatetime(), len(df) 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) df = self._trades_load(pair, trading_mode)
if df.empty: if df.empty:
return ( return (
datetime.fromtimestamp(0, tz=timezone.utc), datetime.fromtimestamp(0, tz=UTC),
datetime.fromtimestamp(0, tz=timezone.utc), datetime.fromtimestamp(0, tz=UTC),
0, 0,
) )
return ( return (
+2 -2
View File
@@ -1,7 +1,7 @@
"""Binance exchange subclass""" """Binance exchange subclass"""
import logging import logging
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
import ccxt import ccxt
@@ -160,7 +160,7 @@ class Binance(Exchange):
since_ms = x[3][0][0] since_ms = x[3][0][0]
logger.info( logger.info(
f"Candle-data for {pair} available starting with " 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: if until_ms and since_ms >= until_ms:
logger.warning( logger.warning(
+2 -2
View File
@@ -1,7 +1,7 @@
"""Bitpanda exchange subclass""" """Bitpanda exchange subclass"""
import logging import logging
from datetime import datetime, timezone from datetime import UTC, datetime
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
@@ -34,5 +34,5 @@ class Bitpanda(Exchange):
:param pair: Pair the order is for :param pair: Pair the order is for
:param since: datetime object of the order creation time. Assumes object is in UTC. :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) return super().get_trades_for_order(order_id, pair, since, params)
+6 -6
View File
@@ -9,7 +9,7 @@ import logging
import signal import signal
from collections.abc import Coroutine, Generator from collections.abc import Coroutine, Generator
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from math import floor, isnan from math import floor, isnan
from threading import Lock from threading import Lock
from typing import Any, Literal, TypeGuard, TypeVar from typing import Any, Literal, TypeGuard, TypeVar
@@ -655,7 +655,7 @@ class Exchange:
if isinstance(markets, Exception): if isinstance(markets, Exception):
raise markets raise markets
return None return None
except asyncio.TimeoutError as e: except TimeoutError as e:
logger.warning("Could not load markets. Reason: %s", e) logger.warning("Could not load markets. Reason: %s", e)
raise TemporaryError from e raise TemporaryError from e
@@ -2220,7 +2220,7 @@ class Exchange:
_params = params if params else {} _params = params if params else {}
my_trades = self._api.fetch_my_trades( my_trades = self._api.fetch_my_trades(
pair, pair,
int((since.replace(tzinfo=timezone.utc).timestamp() - 5) * 1000), int((since.replace(tzinfo=UTC).timestamp() - 5) * 1000),
params=_params, params=_params,
) )
matched_trades = [trade for trade in my_trades if trade["order"] == order_id] 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(): if not filename.parent.is_dir():
filename.parent.mkdir(parents=True) filename.parent.mkdir(parents=True)
data = { data = {
"updated": datetime.now(timezone.utc), "updated": datetime.now(UTC),
"data": tiers, "data": tiers,
} }
file_dump_json(filename, data) file_dump_json(filename, data)
@@ -3369,7 +3369,7 @@ class Exchange:
updated = tiers.get("updated") updated = tiers.get("updated")
if updated: if updated:
updated_dt = parser.parse(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.") logger.info("Cached leverage tiers are outdated. Will update.")
return None return None
return tiers.get("data") return tiers.get("data")
@@ -3593,7 +3593,7 @@ class Exchange:
mark_price_type = CandleType.from_string(self._ft_has["mark_ohlcv_price"]) mark_price_type = CandleType.from_string(self._ft_has["mark_ohlcv_price"])
if not close_date: 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)) since_ms = dt_ts(timeframe_to_prev_date(timeframe, open_date))
mark_comb: PairWithTimeframe = (pair, timeframe, mark_price_type) mark_comb: PairWithTimeframe = (pair, timeframe, mark_price_type)
+2 -2
View File
@@ -3,7 +3,7 @@ Exchange support utils
""" """
import inspect import inspect
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from math import ceil, floor, isnan from math import ceil, floor, isnan
from typing import Any from typing import Any
@@ -148,7 +148,7 @@ def date_minus_candles(timeframe: str, candle_count: int, date: datetime | None
""" """
if not date: if not date:
date = datetime.now(timezone.utc) date = datetime.now(UTC)
tf_min = timeframe_to_minutes(timeframe) tf_min = timeframe_to_minutes(timeframe)
new_date = timeframe_to_prev_date(timeframe, date) - timedelta(minutes=tf_min * candle_count) new_date = timeframe_to_prev_date(timeframe, date) - timedelta(minutes=tf_min * candle_count)
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
import ccxt import ccxt
from ccxt import ROUND_DOWN, ROUND_UP 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) :returns: date of previous candle (with utc timezone)
""" """
if not date: 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 new_timestamp = ccxt.Exchange.round_timeframe(timeframe, dt_ts(date), ROUND_DOWN) // 1000
return dt_from_ts(new_timestamp) 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) :returns: date of next candle (with utc timezone)
""" """
if not date: 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 new_timestamp = ccxt.Exchange.round_timeframe(timeframe, dt_ts(date), ROUND_UP) // 1000
return dt_from_ts(new_timestamp) return dt_from_ts(new_timestamp)
@@ -3,7 +3,7 @@ import importlib
import logging import logging
from abc import abstractmethod from abc import abstractmethod
from collections.abc import Callable from collections.abc import Callable
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -239,7 +239,7 @@ class BaseReinforcementLearningModel(IFreqaiModel):
pair, refresh=False, side="exit", is_short=trade.is_short 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) trade_duration = int((now - trade.open_date_utc.timestamp()) / self.base_tf_seconds)
current_profit = trade.calc_profit_ratio(current_rate) current_profit = trade.calc_profit_ratio(current_rate)
if trade.is_short: if trade.is_short:
+2 -2
View File
@@ -5,7 +5,7 @@ import re
import shutil import shutil
import threading import threading
import warnings import warnings
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import Any, TypedDict from typing import Any, TypedDict
@@ -116,7 +116,7 @@ class FreqaiDataDrawer:
if metric not in self.metric_tracker[pair]: if metric not in self.metric_tracker[pair]:
self.metric_tracker[pair][metric] = {"timestamp": [], "value": []} 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]["value"].append(value)
self.metric_tracker[pair][metric]["timestamp"].append(timestamp) self.metric_tracker[pair][metric]["timestamp"].append(timestamp)
+4 -4
View File
@@ -3,7 +3,7 @@ import inspect
import logging import logging
import random import random
import shutil import shutil
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -341,7 +341,7 @@ class FreqaiDataKitchen:
full_timerange = TimeRange.parse_timerange(tr) full_timerange = TimeRange.parse_timerange(tr)
config_timerange = TimeRange.parse_timerange(self.config["timerange"]) config_timerange = TimeRange.parse_timerange(self.config["timerange"])
if config_timerange.stopts == 0: 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_train = copy.deepcopy(full_timerange)
timerange_backtest = copy.deepcopy(full_timerange) timerange_backtest = copy.deepcopy(full_timerange)
@@ -525,7 +525,7 @@ class FreqaiDataKitchen:
:return: :return:
bool = If the model is expired or not. 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 elapsed_time = (time - trained_timestamp) / 3600 # hours
max_time = self.freqai_config.get("expiration_hours", 0) max_time = self.freqai_config.get("expiration_hours", 0)
if max_time > 0: if max_time > 0:
@@ -536,7 +536,7 @@ class FreqaiDataKitchen:
def check_if_new_training_required( def check_if_new_training_required(
self, trained_timestamp: int self, trained_timestamp: int
) -> tuple[bool, TimeRange, TimeRange]: ) -> tuple[bool, TimeRange, TimeRange]:
time = datetime.now(tz=timezone.utc).timestamp() time = datetime.now(tz=UTC).timestamp()
trained_timerange = TimeRange() trained_timerange = TimeRange()
data_load_timerange = TimeRange() data_load_timerange = TimeRange()
+2 -2
View File
@@ -3,7 +3,7 @@ import threading
import time import time
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from collections import deque from collections import deque
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any, Literal from typing import Any, Literal
@@ -76,7 +76,7 @@ class IFreqaiModel(ABC):
self.dd = FreqaiDataDrawer(Path(self.full_path), self.config) self.dd = FreqaiDataDrawer(Path(self.full_path), self.config)
# set current candle to arbitrary historical date # 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.dd.current_candle = self.current_candle
self.scanning = False self.scanning = False
self.ft_params = self.freqai_info["feature_parameters"] self.ft_params = self.freqai_info["feature_parameters"]
+2 -2
View File
@@ -1,5 +1,5 @@
import logging import logging
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any 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 Used to compute the required data download time range
for auto data-download in FreqAI 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") timeframes = config["freqai"]["feature_parameters"].get("include_timeframes")
+22 -24
View File
@@ -5,7 +5,7 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade()
import logging import logging
import traceback import traceback
from copy import deepcopy from copy import deepcopy
from datetime import datetime, time, timedelta, timezone from datetime import UTC, datetime, time, timedelta
from math import isclose from math import isclose
from threading import Lock from threading import Lock
from time import sleep from time import sleep
@@ -266,7 +266,7 @@ class FreqtradeBot(LoggingMixin):
) )
strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)( 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: with self._measure_execution:
@@ -296,7 +296,7 @@ class FreqtradeBot(LoggingMixin):
self._schedule.run_pending() self._schedule.run_pending()
Trade.commit() Trade.commit()
self.rpc.process_msg_queue(self.dataprovider._msg_queue) 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: def process_stopped(self) -> None:
""" """
@@ -421,7 +421,7 @@ class FreqtradeBot(LoggingMixin):
except InvalidOrderException as e: except InvalidOrderException as e:
logger.warning(f"Error updating Order {order.order_id} due to {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( logger.warning(
"Order is older than 5 days. Assuming order was fully cancelled." "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}") logger.debug(f"Calling adjust_trade_position for pair {trade.pair}")
stake_amount, order_tag = self.strategy._adjust_trade_position_internal( stake_amount, order_tag = self.strategy._adjust_trade_position_internal(
trade=trade, trade=trade,
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
current_rate=current_entry_rate, current_rate=current_entry_rate,
current_profit=current_entry_profit, current_profit=current_entry_profit,
min_stake=min_entry_stake, min_stake=min_entry_stake,
@@ -916,7 +916,7 @@ class FreqtradeBot(LoggingMixin):
amount=amount, amount=amount,
rate=enter_limit_requested, rate=enter_limit_requested,
time_in_force=time_in_force, time_in_force=time_in_force,
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
entry_tag=enter_tag, entry_tag=enter_tag,
side=trade_side, side=trade_side,
): ):
@@ -987,7 +987,7 @@ class FreqtradeBot(LoggingMixin):
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
fee = self.exchange.get_fee(symbol=pair, taker_or_maker="maker") fee = self.exchange.get_fee(symbol=pair, taker_or_maker="maker")
base_currency = self.exchange.get_pair_base_currency(pair) 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( funding_fees = self.exchange.get_funding_fees(
pair=pair, pair=pair,
@@ -1106,7 +1106,7 @@ class FreqtradeBot(LoggingMixin):
)( )(
pair=pair, pair=pair,
trade=trade, trade=trade,
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
proposed_rate=enter_limit_requested, proposed_rate=enter_limit_requested,
entry_tag=entry_tag, entry_tag=entry_tag,
side=trade_side, side=trade_side,
@@ -1124,7 +1124,7 @@ class FreqtradeBot(LoggingMixin):
else: else:
leverage = strategy_safe_wrapper(self.strategy.leverage, default_retval=1.0)( leverage = strategy_safe_wrapper(self.strategy.leverage, default_retval=1.0)(
pair=pair, pair=pair,
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
current_rate=enter_limit_requested, current_rate=enter_limit_requested,
proposed_leverage=1.0, proposed_leverage=1.0,
max_leverage=max_leverage, max_leverage=max_leverage,
@@ -1157,7 +1157,7 @@ class FreqtradeBot(LoggingMixin):
self.strategy.custom_stake_amount, default_retval=stake_amount self.strategy.custom_stake_amount, default_retval=stake_amount
)( )(
pair=pair, pair=pair,
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
current_rate=enter_limit_requested, current_rate=enter_limit_requested,
proposed_stake=stake_amount, proposed_stake=stake_amount,
min_stake=min_stake_amount, min_stake=min_stake_amount,
@@ -1222,7 +1222,7 @@ class FreqtradeBot(LoggingMixin):
"quote_currency": self.exchange.get_pair_quote_currency(trade.pair), "quote_currency": self.exchange.get_pair_quote_currency(trade.pair),
"fiat_currency": self.config.get("fiat_display_currency", None), "fiat_currency": self.config.get("fiat_display_currency", None),
"amount": order.safe_amount_after_fee if fill else (order.safe_amount or trade.amount), "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, "current_rate": current_rate,
"sub_trade": sub_trade, "sub_trade": sub_trade,
} }
@@ -1361,7 +1361,7 @@ class FreqtradeBot(LoggingMixin):
exits: list[ExitCheckTuple] = self.strategy.should_exit( exits: list[ExitCheckTuple] = self.strategy.should_exit(
trade, trade,
exit_rate, exit_rate,
datetime.now(timezone.utc), datetime.now(UTC),
enter=enter, enter=enter,
exit_=exit_, exit_=exit_,
force_stoploss=0, force_stoploss=0,
@@ -1496,7 +1496,7 @@ class FreqtradeBot(LoggingMixin):
if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side): if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side):
# we check if the update is necessary # we check if the update is necessary
update_beat = self.strategy.order_types.get("stoploss_on_exchange_interval", 60) 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: if trade.stoploss_last_update_utc and upd_req >= trade.stoploss_last_update_utc:
# cancelling the current stoploss on exchange first # cancelling the current stoploss on exchange first
logger.info( logger.info(
@@ -1583,9 +1583,7 @@ class FreqtradeBot(LoggingMixin):
if not_closed: if not_closed:
if fully_cancelled or ( if fully_cancelled or (
open_order open_order
and self.strategy.ft_check_timed_out( and self.strategy.ft_check_timed_out(trade, open_order, datetime.now(UTC))
trade, open_order, datetime.now(timezone.utc)
)
): ):
self.handle_cancel_order( self.handle_cancel_order(
order, open_order, trade, constants.CANCEL_REASON["TIMEOUT"] order, open_order, trade, constants.CANCEL_REASON["TIMEOUT"]
@@ -1683,7 +1681,7 @@ class FreqtradeBot(LoggingMixin):
trade=trade, trade=trade,
order=order_obj, order=order_obj,
pair=trade.pair, pair=trade.pair,
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
proposed_rate=proposed_rate, proposed_rate=proposed_rate,
current_order_rate=order_obj.safe_placement_price, current_order_rate=order_obj.safe_placement_price,
entry_tag=trade.enter_tag, entry_tag=trade.enter_tag,
@@ -2075,7 +2073,7 @@ class FreqtradeBot(LoggingMixin):
)( )(
pair=trade.pair, pair=trade.pair,
trade=trade, trade=trade,
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
proposed_rate=proposed_limit_rate, proposed_rate=proposed_limit_rate,
current_profit=current_profit, current_profit=current_profit,
exit_tag=exit_reason, exit_tag=exit_reason,
@@ -2106,7 +2104,7 @@ class FreqtradeBot(LoggingMixin):
time_in_force=time_in_force, time_in_force=time_in_force,
exit_reason=exit_reason, exit_reason=exit_reason,
sell_reason=exit_reason, # sellreason -> compatibility 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}.") logger.info(f"User denied exit for {trade.pair}.")
@@ -2202,7 +2200,7 @@ class FreqtradeBot(LoggingMixin):
"enter_tag": trade.enter_tag, "enter_tag": trade.enter_tag,
"exit_reason": trade.exit_reason, "exit_reason": trade.exit_reason,
"open_date": trade.open_date_utc, "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_amount": trade.stake_amount,
"stake_currency": self.config["stake_currency"], "stake_currency": self.config["stake_currency"],
"base_currency": self.exchange.get_pair_base_currency(trade.pair), "base_currency": self.exchange.get_pair_base_currency(trade.pair),
@@ -2257,7 +2255,7 @@ class FreqtradeBot(LoggingMixin):
"enter_tag": trade.enter_tag, "enter_tag": trade.enter_tag,
"exit_reason": trade.exit_reason, "exit_reason": trade.exit_reason,
"open_date": trade.open_date, "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"], "stake_currency": self.config["stake_currency"],
"base_currency": self.exchange.get_pair_base_currency(trade.pair), "base_currency": self.exchange.get_pair_base_currency(trade.pair),
"quote_currency": self.exchange.get_pair_quote_currency(trade.pair), "quote_currency": self.exchange.get_pair_quote_currency(trade.pair),
@@ -2338,7 +2336,7 @@ class FreqtradeBot(LoggingMixin):
def _update_trade_after_fill(self, trade: Trade, order: Order, send_msg: bool) -> Trade: def _update_trade_after_fill(self, trade: Trade, order: Order, send_msg: bool) -> Trade:
if order.status in constants.NON_OPEN_EXCHANGE_STATES: if order.status in constants.NON_OPEN_EXCHANGE_STATES:
strategy_safe_wrapper(self.strategy.order_filled, default_retval=None)( 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 a entry order was closed, force update on stoploss on exchange
if order.ft_order_side == trade.entry_side: if order.ft_order_side == trade.entry_side:
@@ -2371,7 +2369,7 @@ class FreqtradeBot(LoggingMixin):
) )
profit = trade.calc_profit_ratio(current_rate) profit = trade.calc_profit_ratio(current_rate)
self.strategy.ft_stoploss_adjust( 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 # Updating wallets when order is closed
self.wallets.update() self.wallets.update()
@@ -2397,7 +2395,7 @@ class FreqtradeBot(LoggingMixin):
def handle_protections(self, pair: str, side: LongShort) -> None: def handle_protections(self, pair: str, side: LongShort) -> None:
# Lock pair for one candle to prevent immediate re-entries # 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) prot_trig = self.protections.stop_per_pair(pair, side=side)
if prot_trig: if prot_trig:
msg: RPCProtectionMsg = { msg: RPCProtectionMsg = {
+2 -2
View File
@@ -1,8 +1,8 @@
from datetime import datetime from datetime import datetime
from typing import Literal from typing import Literal, Required
from pydantic import TypeAdapter from pydantic import TypeAdapter
from typing_extensions import Required, TypedDict from typing_extensions import TypedDict
class AnnotationType(TypedDict, total=False): class AnnotationType(TypedDict, total=False):
+2 -2
View File
@@ -10,8 +10,8 @@ from typing import Any
# check min. python version # check min. python version
if sys.version_info < (3, 10): # pragma: no cover # noqa: UP036 if sys.version_info < (3, 11): # pragma: no cover # noqa: UP036
sys.exit("Freqtrade requires Python version >= 3.10") sys.exit("Freqtrade requires Python version >= 3.11")
from freqtrade import __version__ from freqtrade import __version__
from freqtrade.commands import Arguments from freqtrade.commands import Arguments
+4 -4
View File
@@ -1,6 +1,6 @@
import logging import logging
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timezone from datetime import UTC, datetime
from typing import Any from typing import Any
from pandas import DataFrame from pandas import DataFrame
@@ -38,7 +38,7 @@ class BaseAnalysis:
@staticmethod @staticmethod
def dt_to_timestamp(dt: datetime): def dt_to_timestamp(dt: datetime):
timestamp = int(dt.replace(tzinfo=timezone.utc).timestamp()) timestamp = int(dt.replace(tzinfo=UTC).timestamp())
return timestamp return timestamp
def fill_full_varholder(self): def fill_full_varholder(self):
@@ -48,12 +48,12 @@ class BaseAnalysis:
parsed_timerange = TimeRange.parse_timerange(self.local_config["timerange"]) parsed_timerange = TimeRange.parse_timerange(self.local_config["timerange"])
if parsed_timerange.startdt is None: 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: else:
self.full_varHolder.from_dt = parsed_timerange.startdt self.full_varHolder.from_dt = parsed_timerange.startdt
if parsed_timerange.stopdt is None: if parsed_timerange.stopdt is None:
self.full_varHolder.to_dt = datetime.now(timezone.utc) self.full_varHolder.to_dt = datetime.now(UTC)
else: else:
self.full_varHolder.to_dt = parsed_timerange.stopdt self.full_varHolder.to_dt = parsed_timerange.stopdt
@@ -6,7 +6,7 @@ and will be sent to the hyperopt worker processes.
import logging import logging
import sys import sys
import warnings import warnings
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -273,7 +273,7 @@ class HyperOptimizer:
Keep this function as optimized as possible! Keep this function as optimized as possible!
""" """
HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE) HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE)
backtest_start_time = datetime.now(timezone.utc) backtest_start_time = datetime.now(UTC)
# Apply parameters # Apply parameters
if HyperoptTools.has_space(self.config, "buy"): if HyperoptTools.has_space(self.config, "buy"):
@@ -330,7 +330,7 @@ class HyperOptimizer:
bt_results = self.backtesting.backtest( bt_results = self.backtesting.backtest(
processed=processed, start_date=self.min_date, end_date=self.max_date 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( bt_results.update(
{ {
"backtest_start_time": int(backtest_start_time.timestamp()), "backtest_start_time": int(backtest_start_time.timestamp()),
+2 -2
View File
@@ -1,7 +1,7 @@
import logging import logging
from collections.abc import Iterator from collections.abc import Iterator
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@@ -71,7 +71,7 @@ class HyperoptTools:
"strategy_name": strategy_name, "strategy_name": strategy_name,
"params": final_params, "params": final_params,
"ft_stratparam_v": 1, "ft_stratparam_v": 1,
"export_time": datetime.now(timezone.utc), "export_time": datetime.now(UTC),
} }
logger.info(f"Dumping parameters to {filename}") logger.info(f"Dumping parameters to {filename}")
with filename.open("w") as f: with filename.open("w") as f:
@@ -1,6 +1,6 @@
import logging import logging
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from typing import Any, Literal from typing import Any, Literal
import numpy as np import numpy as np
@@ -652,9 +652,9 @@ def generate_strategy_stats(
"max_drawdown_abs": 0.0, "max_drawdown_abs": 0.0,
"max_drawdown_low": 0.0, "max_drawdown_low": 0.0,
"max_drawdown_high": 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_start_ts": 0,
"drawdown_end": datetime(1970, 1, 1, tzinfo=timezone.utc), "drawdown_end": datetime(1970, 1, 1, tzinfo=UTC),
"drawdown_end_ts": 0, "drawdown_end_ts": 0,
"csum_min": 0, "csum_min": 0,
"csum_max": 0, "csum_max": 0,
+5 -5
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from enum import Enum from enum import Enum
from typing import ClassVar, Literal from typing import ClassVar, Literal
@@ -114,7 +114,7 @@ class KeyValueStore:
if kv.value_type == ValueTypesEnum.STRING: if kv.value_type == ValueTypesEnum.STRING:
return kv.string_value return kv.string_value
if kv.value_type == ValueTypesEnum.DATETIME and kv.datetime_value is not None: 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: if kv.value_type == ValueTypesEnum.FLOAT:
return kv.float_value return kv.float_value
if kv.value_type == ValueTypesEnum.INT: if kv.value_type == ValueTypesEnum.INT:
@@ -156,7 +156,7 @@ class KeyValueStore:
) )
if kv is None or kv.datetime_value is None: if kv is None or kv.datetime_value is None:
return None return None
return kv.datetime_value.replace(tzinfo=timezone.utc) return kv.datetime_value.replace(tzinfo=UTC)
@staticmethod @staticmethod
def get_float_value(key: KeyStoreKeys) -> float | None: def get_float_value(key: KeyStoreKeys) -> float | None:
@@ -207,5 +207,5 @@ def set_startup_time() -> None:
if t is not None: if t is not None:
KeyValueStore.store_value("bot_start_time", t.open_date_utc) KeyValueStore.store_value("bot_start_time", t.open_date_utc)
else: else:
KeyValueStore.store_value("bot_start_time", datetime.now(timezone.utc)) KeyValueStore.store_value("bot_start_time", datetime.now(UTC))
KeyValueStore.store_value("startup_time", datetime.now(timezone.utc)) KeyValueStore.store_value("startup_time", datetime.now(UTC))
+3 -5
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from typing import Any, ClassVar from typing import Any, ClassVar
from sqlalchemy import ScalarResult, String, or_, select from sqlalchemy import ScalarResult, String, or_, select
@@ -69,11 +69,9 @@ class PairLock(ModelBase):
"id": self.id, "id": self.id,
"pair": self.pair, "pair": self.pair,
"lock_time": self.lock_time.strftime(DATETIME_PRINT_FORMAT), "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_time": self.lock_end_time.strftime(DATETIME_PRINT_FORMAT),
"lock_end_timestamp": int( "lock_end_timestamp": int(self.lock_end_time.replace(tzinfo=UTC).timestamp() * 1000),
self.lock_end_time.replace(tzinfo=timezone.utc).timestamp() * 1000
),
"reason": self.reason, "reason": self.reason,
"side": self.side, "side": self.side,
"active": self.active, "active": self.active,
+7 -7
View File
@@ -1,6 +1,6 @@
import logging import logging
from collections.abc import Sequence from collections.abc import Sequence
from datetime import datetime, timezone from datetime import UTC, datetime
from sqlalchemy import select from sqlalchemy import select
@@ -52,7 +52,7 @@ class PairLocks:
""" """
lock = PairLock( lock = PairLock(
pair=pair, 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), lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until),
reason=reason, reason=reason,
side=side, side=side,
@@ -77,7 +77,7 @@ class PairLocks:
:param side: Side get locks for, can be 'long', 'short', '*' or None :param side: Side get locks for, can be 'long', 'short', '*' or None
""" """
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(UTC)
if PairLocks.use_db: if PairLocks.use_db:
return PairLock.query_pair_locks(pair, now, side).all() return PairLock.query_pair_locks(pair, now, side).all()
@@ -114,7 +114,7 @@ class PairLocks:
defaults to datetime.now(timezone.utc) defaults to datetime.now(timezone.utc)
""" """
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(UTC)
logger.info(f"Releasing all locks for {pair}.") logger.info(f"Releasing all locks for {pair}.")
locks = PairLocks.get_pair_locks(pair, now, side=side) locks = PairLocks.get_pair_locks(pair, now, side=side)
@@ -132,7 +132,7 @@ class PairLocks:
defaults to datetime.now(timezone.utc) defaults to datetime.now(timezone.utc)
""" """
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(UTC)
if PairLocks.use_db: if PairLocks.use_db:
# used in live modes # used in live modes
@@ -161,7 +161,7 @@ class PairLocks:
defaults to datetime.now(timezone.utc) defaults to datetime.now(timezone.utc)
""" """
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(UTC)
return len(PairLocks.get_pair_locks("*", now, side)) > 0 return len(PairLocks.get_pair_locks("*", now, side)) > 0
@@ -173,7 +173,7 @@ class PairLocks:
defaults to datetime.now(timezone.utc) defaults to datetime.now(timezone.utc)
""" """
if not now: 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( return len(PairLocks.get_pair_locks(pair, now, side)) > 0 or PairLocks.is_global_lock(
now, side now, side
+12 -15
View File
@@ -6,9 +6,9 @@ import logging
from collections import defaultdict from collections import defaultdict
from collections.abc import Sequence from collections.abc import Sequence
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import UTC, datetime
from math import isclose from math import isclose
from typing import Any, ClassVar, Optional, cast from typing import Any, ClassVar, Optional, Self, cast
from sqlalchemy import ( from sqlalchemy import (
Enum, Enum,
@@ -25,7 +25,6 @@ from sqlalchemy import (
select, select,
) )
from sqlalchemy.orm import Mapped, lazyload, mapped_column, relationship, validates from sqlalchemy.orm import Mapped, lazyload, mapped_column, relationship, validates
from typing_extensions import Self
from freqtrade.constants import ( from freqtrade.constants import (
CANCELED_EXCHANGE_STATES, CANCELED_EXCHANGE_STATES,
@@ -121,14 +120,12 @@ class Order(ModelBase):
@property @property
def order_date_utc(self) -> datetime: def order_date_utc(self) -> datetime:
"""Order-date with UTC timezoneinfo""" """Order-date with UTC timezoneinfo"""
return self.order_date.replace(tzinfo=timezone.utc) return self.order_date.replace(tzinfo=UTC)
@property @property
def order_filled_utc(self) -> datetime | None: def order_filled_utc(self) -> datetime | None:
"""last order-date with UTC timezoneinfo""" """last order-date with UTC timezoneinfo"""
return ( return self.order_filled_date.replace(tzinfo=UTC) if self.order_filled_date else None
self.order_filled_date.replace(tzinfo=timezone.utc) if self.order_filled_date else None
)
@property @property
def safe_amount(self) -> float: def safe_amount(self) -> float:
@@ -229,7 +226,7 @@ class Order(ModelBase):
self.order_filled_date = dt_from_ts( self.order_filled_date = dt_from_ts(
safe_value_fallback(order, "lastTradeTimestamp", default_value=dt_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]: def to_ccxt_object(self, stopPriceName: str = "stopPrice") -> dict[str, Any]:
order: dict[str, Any] = { order: dict[str, Any] = {
@@ -286,7 +283,7 @@ class Order(ModelBase):
self.order_date.strftime(DATETIME_PRINT_FORMAT) if self.order_date else None self.order_date.strftime(DATETIME_PRINT_FORMAT) if self.order_date else None
), ),
"order_timestamp": ( "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 if self.order_date
else None else None
), ),
@@ -533,7 +530,7 @@ class LocalTrade:
@property @property
def open_date_utc(self): def open_date_utc(self):
return self.open_date.replace(tzinfo=timezone.utc) return self.open_date.replace(tzinfo=UTC)
@property @property
def stoploss_last_update_utc(self): def stoploss_last_update_utc(self):
@@ -543,7 +540,7 @@ class LocalTrade:
@property @property
def close_date_utc(self): 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 @property
def entry_side(self) -> str: def entry_side(self) -> str:
@@ -1056,7 +1053,7 @@ class LocalTrade:
return zero return zero
open_date = self.open_date.replace(tzinfo=None) 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) sec_per_hour = FtPrecise(3600)
total_seconds = FtPrecise((now - open_date).total_seconds()) total_seconds = FtPrecise((now - open_date).total_seconds())
hours = total_seconds / sec_per_hour or zero hours = total_seconds / sec_per_hour or zero
@@ -1572,12 +1569,12 @@ class LocalTrade:
fee_close=data["fee_close"], fee_close=data["fee_close"],
fee_close_cost=data.get("fee_close_cost"), fee_close_cost=data.get("fee_close_cost"),
fee_close_currency=data.get("fee_close_currency"), 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=data["open_rate"],
open_rate_requested=data.get("open_rate_requested", data["open_rate"]), open_rate_requested=data.get("open_rate_requested", data["open_rate"]),
open_trade_value=data.get("open_trade_value"), open_trade_value=data.get("open_trade_value"),
close_date=( close_date=(
datetime.fromtimestamp(data["close_timestamp"] // 1000, tz=timezone.utc) datetime.fromtimestamp(data["close_timestamp"] // 1000, tz=UTC)
if data["close_timestamp"] if data["close_timestamp"]
else None else None
), ),
@@ -1622,7 +1619,7 @@ class LocalTrade:
if order.get("order_date") if order.get("order_date")
else None, else None,
order_filled_date=( 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"] if order["order_filled_timestamp"]
else None else None
), ),
+2 -2
View File
@@ -1,5 +1,5 @@
import logging import logging
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
import pandas as pd import pandas as pd
@@ -638,7 +638,7 @@ def load_and_plot_trades(config: Config):
exchange = ExchangeResolver.load_exchange(config) exchange = ExchangeResolver.load_exchange(config)
IStrategy.dp = DataProvider(config, exchange) IStrategy.dp = DataProvider(config, exchange)
strategy.ft_bot_start() 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) plot_elements = init_plotscript(config, list(exchange.markets), strategy.startup_candle_count)
timerange = plot_elements["timerange"] timerange = plot_elements["timerange"]
trades = plot_elements["trades"] trades = plot_elements["trades"]
+3 -3
View File
@@ -3,7 +3,7 @@ Protection manager class
""" """
import logging import logging
from datetime import datetime, timezone from datetime import UTC, datetime
from typing import Any from typing import Any
from freqtrade.constants import Config, LongShort 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: def global_stop(self, now: datetime | None = None, side: LongShort = "long") -> PairLock | None:
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(UTC)
result = None result = None
for protection_handler in self._protection_handlers: for protection_handler in self._protection_handlers:
if protection_handler.has_global_stop: if protection_handler.has_global_stop:
@@ -65,7 +65,7 @@ class ProtectionManager:
self, pair, now: datetime | None = None, side: LongShort = "long" self, pair, now: datetime | None = None, side: LongShort = "long"
) -> PairLock | None: ) -> PairLock | None:
if not now: if not now:
now = datetime.now(timezone.utc) now = datetime.now(UTC)
result = None result = None
for protection_handler in self._protection_handlers: for protection_handler in self._protection_handlers:
if protection_handler.has_local_stop: if protection_handler.has_local_stop:
+2 -2
View File
@@ -1,7 +1,7 @@
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from typing import Any from typing import Any
from freqtrade.constants import Config, LongShort 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]) max_date: datetime = max([trade.close_date for trade in trades if trade.close_date])
# coming from Database, tzinfo is not set. # coming from Database, tzinfo is not set.
if max_date.tzinfo is None: 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: if self._unlock_at is not None:
# unlock_at case with fixed hour of the day # unlock_at case with fixed hour of the day
+4 -4
View File
@@ -1,6 +1,6 @@
import logging import logging
import secrets import secrets
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from typing import Any from typing import Any
import jwt 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 def create_token(data: dict, secret_key: str, token_type: str = "access") -> str: # noqa: S107
to_encode = data.copy() to_encode = data.copy()
if token_type == "access": # noqa: S105 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 elif token_type == "refresh": # noqa: S105
expire = datetime.now(timezone.utc) + timedelta(days=30) expire = datetime.now(UTC) + timedelta(days=30)
else: else:
raise ValueError() raise ValueError()
to_encode.update( to_encode.update(
{ {
"exp": expire, "exp": expire,
"iat": datetime.now(timezone.utc), "iat": datetime.now(UTC),
"type": token_type, "type": token_type,
} }
) )
+2 -2
View File
@@ -1,7 +1,7 @@
from typing import Any, Literal from typing import Any, Literal, NotRequired
from uuid import uuid4 from uuid import uuid4
from typing_extensions import NotRequired, TypedDict from typing_extensions import TypedDict
from freqtrade.exchange.exchange import Exchange from freqtrade.exchange.exchange import Exchange
+2 -2
View File
@@ -102,7 +102,7 @@ class WebSocketChannel:
self._send_times.append(total_time) self._send_times.append(total_time)
self._calc_send_limit() self._calc_send_limit()
except asyncio.TimeoutError: except TimeoutError:
logger.info(f"Connection for {self} timed out, disconnecting") logger.info(f"Connection for {self} timed out, disconnecting")
raise raise
@@ -201,8 +201,8 @@ class WebSocketChannel:
try: try:
await task await task
except ( except (
TimeoutError,
asyncio.CancelledError, asyncio.CancelledError,
asyncio.TimeoutError,
WebSocketDisconnect, WebSocketDisconnect,
ConnectionClosed, ConnectionClosed,
RuntimeError, RuntimeError,
+1 -1
View File
@@ -266,7 +266,7 @@ class ExternalMessageConsumer:
except Exception as e: except Exception as e:
logger.exception(f"Error handling producer message: {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. # We haven't received data yet. Check the connection and continue.
try: try:
# ping # ping
+3 -3
View File
@@ -5,7 +5,7 @@ This module contains class to define a RPC communications
import logging import logging
from abc import abstractmethod from abc import abstractmethod
from collections.abc import Generator, Sequence 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 from typing import TYPE_CHECKING, Any
import psutil import psutil
@@ -375,7 +375,7 @@ class RPC:
""" """
:param timeunit: Valid entries are 'days', 'weeks', 'months' :param timeunit: Valid entries are 'days', 'weeks', 'months'
""" """
start_date = datetime.now(timezone.utc).date() start_date = datetime.now(UTC).date()
if timeunit == "weeks": if timeunit == "weeks":
# weekly # weekly
start_date = start_date - timedelta(days=start_date.weekday()) # Monday start_date = start_date - timedelta(days=start_date.weekday()) # Monday
@@ -1259,7 +1259,7 @@ class RPC:
for lock in locks: for lock in locks:
lock.active = False lock.active = False
lock.lock_end_time = datetime.now(timezone.utc) lock.lock_end_time = datetime.now(UTC)
Trade.commit() Trade.commit()
+3 -3
View File
@@ -5,7 +5,7 @@ This module defines the interface to apply for strategies
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from math import isinf, isnan from math import isinf, isnan
from pandas import DataFrame 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. manually from within the strategy, to allow an easy way to unlock pairs.
:param pair: Unlock pair to allow trading again :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: 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. manually from within the strategy, to allow an easy way to unlock pairs.
:param reason: Unlock pairs to allow trading again :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( def is_pair_locked(
self, pair: str, *, candle_date: datetime | None = None, side: str = "*" self, pair: str, *, candle_date: datetime | None = None, side: str = "*"
+4 -4
View File
@@ -1,5 +1,5 @@
import re import re
from datetime import datetime, timezone from datetime import UTC, datetime
from time import time from time import time
import humanize import humanize
@@ -9,7 +9,7 @@ from freqtrade.constants import DATETIME_PRINT_FORMAT
def dt_now() -> datetime: def dt_now() -> datetime:
"""Return the current datetime in UTC.""" """Return the current datetime in UTC."""
return datetime.now(timezone.utc) return datetime.now(UTC)
def dt_utc( def dt_utc(
@@ -22,7 +22,7 @@ def dt_utc(
microsecond: int = 0, microsecond: int = 0,
) -> datetime: ) -> datetime:
"""Return a datetime in UTC.""" """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: def dt_ts(dt: datetime | None = None) -> int:
@@ -68,7 +68,7 @@ def dt_from_ts(timestamp: float) -> datetime:
if timestamp > 1e10: if timestamp > 1e10:
# Timezone in ms - convert to seconds # Timezone in ms - convert to seconds
timestamp /= 1000 timestamp /= 1000
return datetime.fromtimestamp(timestamp, tz=timezone.utc) return datetime.fromtimestamp(timestamp, tz=UTC)
def shorten_date(_date: str) -> str: def shorten_date(_date: str) -> str:
+2 -2
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from cachetools import TTLCache from cachetools import TTLCache
@@ -11,7 +11,7 @@ class PeriodicCache(TTLCache):
def __init__(self, maxsize, ttl, getsizeof=None): def __init__(self, maxsize, ttl, getsizeof=None):
def local_timer(): def local_timer():
ts = datetime.now(timezone.utc).timestamp() ts = datetime.now(UTC).timestamp()
offset = ts % ttl offset = ts % ttl
return ts - offset return ts - offset
+1 -2
View File
@@ -13,14 +13,13 @@ authors = [
description = "Freqtrade - Client scripts" description = "Freqtrade - Client scripts"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.11"
license = {text = "GPLv3"} license = {text = "GPLv3"}
# license = "GPLv3" # license = "GPLv3"
classifiers = [ classifiers = [
"Environment :: Console", "Environment :: Console",
"Intended Audience :: Science/Research", "Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.13",
+1 -2
View File
@@ -13,13 +13,12 @@ authors = [
description = "Freqtrade - Crypto Trading Bot" description = "Freqtrade - Crypto Trading Bot"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10" requires-python = ">=3.11"
license = {text = "GPLv3"} license = {text = "GPLv3"}
classifiers = [ classifiers = [
"Environment :: Console", "Environment :: Console",
"Intended Audience :: Science/Research", "Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.13",
+1 -1
View File
@@ -2,7 +2,7 @@
-r requirements.txt -r requirements.txt
# Required for hyperopt # Required for hyperopt
scipy==1.15.3 scipy==1.16.0
scikit-learn==1.7.0 scikit-learn==1.7.0
filelock==3.18.0 filelock==3.18.0
optuna==4.4.0 optuna==4.4.0
+1 -1
View File
@@ -1,4 +1,4 @@
numpy==2.2.6 numpy==2.3.1
pandas==2.3.0 pandas==2.3.0
bottleneck==1.5.0 bottleneck==1.5.0
numexpr==2.11.0 numexpr==2.11.0
+2 -2
View File
@@ -234,7 +234,7 @@ async def create_client(
await protocol.on_message(ws, name, message) await protocol.on_message(ws, name, message)
except (asyncio.TimeoutError, websockets.exceptions.WebSocketException): except (TimeoutError, websockets.exceptions.WebSocketException):
# Try pinging # Try pinging
try: try:
pong = await ws.ping() pong = await ws.ping()
@@ -244,7 +244,7 @@ async def create_client(
continue continue
except asyncio.TimeoutError: except TimeoutError:
logger.error(f"Ping timed out, retrying in {sleep_time}s") logger.error(f"Ping timed out, retrying in {sleep_time}s")
await asyncio.sleep(sleep_time) await asyncio.sleep(sleep_time)
+3 -6
View File
@@ -153,16 +153,13 @@ function Find-PythonExecutable {
"python3.13", "python3.13",
"python3.12", "python3.12",
"python3.11", "python3.11",
"python3.10",
"python3", "python3",
"C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python313\python.exe", "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\Python312\python.exe",
"C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python311\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:\Python313\python.exe",
"C:\Python312\python.exe", "C:\Python312\python.exe",
"C:\Python311\python.exe", "C:\Python311\python.exe"
"C:\Python310\python.exe"
) )
@@ -178,10 +175,10 @@ function Main {
"Starting the operations..." | Out-File $LogFilePath -Append "Starting the operations..." | Out-File $LogFilePath -Append
"Current directory: $(Get-Location)" | 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 $PythonExecutable = Find-PythonExecutable
if ($null -eq $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 Exit 1
} }
+4 -4
View File
@@ -25,7 +25,7 @@ function check_installed_python() {
exit 2 exit 2
fi fi
for v in 13 12 11 10 for v in 13 12 11
do do
PYTHON="python3.${v}" PYTHON="python3.${v}"
which $PYTHON which $PYTHON
@@ -36,7 +36,7 @@ function check_installed_python() {
fi fi
done 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 exit 1
} }
@@ -257,7 +257,7 @@ function install() {
install_redhat install_redhat
else else
echo "This script does not support your OS." 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." echo "Wait 10 seconds to continue the next install steps or use ctrl+c to interrupt this shell."
sleep 10 sleep 10
fi fi
@@ -284,7 +284,7 @@ function help() {
echo " -p,--plot Install dependencies for Plotting scripts." echo " -p,--plot Install dependencies for Plotting scripts."
} }
# Verify if 3.10+ is installed # Verify if 3.11+ is installed
check_installed_python check_installed_python
case $* in case $* in
+2 -2
View File
@@ -4,7 +4,7 @@ import logging
import platform import platform
import re import re
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock, Mock, PropertyMock 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): def generate_trades_history(n_rows, start_date: datetime | None = None, days=5):
np.random.seed(42) np.random.seed(42)
if not start_date: if not start_date:
start_date = datetime(2020, 1, 1, tzinfo=timezone.utc) start_date = datetime(2020, 1, 1, tzinfo=UTC)
# Generate random data # Generate random data
end_date = start_date + timedelta(days=days) end_date = start_date + timedelta(days=days)
+12 -12
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from freqtrade.persistence.models import Order, Trade 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_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
is_open=True, 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, open_rate=0.123,
exchange="binance", exchange="binance",
strategy="StrategyTestV3", strategy="StrategyTestV3",
@@ -106,8 +106,8 @@ def mock_trade_2(fee, is_short: bool):
timeframe=5, timeframe=5,
enter_tag="TEST1", enter_tag="TEST1",
exit_reason="sell_signal", 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), close_date=datetime.now(tz=UTC) - timedelta(minutes=2),
is_short=is_short, is_short=is_short,
) )
o = Order.parse_from_ccxt_object(mock_order_2(is_short), "ETC/BTC", entry_side(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", strategy="StrategyTestV3",
timeframe=5, timeframe=5,
exit_reason="roi", exit_reason="roi",
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), close_date=datetime.now(tz=UTC),
is_short=is_short, is_short=is_short,
) )
o = Order.parse_from_ccxt_object(mock_order_3(is_short), "XRP/BTC", entry_side(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, amount_requested=124.0,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=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, is_open=True,
open_rate=0.123, open_rate=0.123,
exchange="binance", exchange="binance",
@@ -260,7 +260,7 @@ def mock_trade_5(fee, is_short: bool):
amount_requested=124.0, amount_requested=124.0,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=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, is_open=True,
open_rate=0.123, open_rate=0.123,
exchange="binance", exchange="binance",
@@ -316,7 +316,7 @@ def mock_trade_6(fee, is_short: bool):
stake_amount=0.001, stake_amount=0.001,
amount=2.0, amount=2.0,
amount_requested=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_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
is_open=True, is_open=True,
@@ -410,7 +410,7 @@ def short_trade(fee):
strategy="DefaultStrategy", strategy="DefaultStrategy",
timeframe=5, timeframe=5,
exit_reason="sell_signal", 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), # close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
is_short=True, is_short=True,
) )
@@ -500,8 +500,8 @@ def leverage_trade(fee):
strategy="DefaultStrategy", strategy="DefaultStrategy",
timeframe=5, timeframe=5,
exit_reason="sell_signal", exit_reason="sell_signal",
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=300), open_date=datetime.now(tz=UTC) - timedelta(minutes=300),
close_date=datetime.now(tz=timezone.utc), close_date=datetime.now(tz=UTC),
interest_rate=0.0005, interest_rate=0.0005,
) )
o = Order.parse_from_ccxt_object(leverage_order(), "DOGE/BTC", "sell") o = Order.parse_from_ccxt_object(leverage_order(), "DOGE/BTC", "sell")
+11 -11
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from freqtrade.persistence.models import Order, Trade from freqtrade.persistence.models import Order, Trade
@@ -55,8 +55,8 @@ def mock_trade_usdt_1(fee, is_short: bool):
stake_amount=20.0, stake_amount=20.0,
amount=2.0, amount=2.0,
amount_requested=2.0, amount_requested=2.0,
open_date=datetime.now(tz=timezone.utc) - timedelta(days=2, minutes=20), open_date=datetime.now(tz=UTC) - timedelta(days=2, minutes=20),
close_date=datetime.now(tz=timezone.utc) - timedelta(days=2, minutes=5), close_date=datetime.now(tz=UTC) - timedelta(days=2, minutes=5),
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
is_open=False, is_open=False,
@@ -127,8 +127,8 @@ def mock_trade_usdt_2(fee, is_short: bool):
timeframe=5, timeframe=5,
enter_tag="TEST1", enter_tag="TEST1",
exit_reason="exit_signal", exit_reason="exit_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), close_date=datetime.now(tz=UTC) - timedelta(minutes=2),
is_short=is_short, is_short=is_short,
) )
o = Order.parse_from_ccxt_object(mock_order_usdt_2(is_short), "NEO/USDT", entry_side(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, timeframe=5,
enter_tag="TEST3", enter_tag="TEST3",
exit_reason="roi", exit_reason="roi",
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), close_date=datetime.now(tz=UTC),
is_short=is_short, is_short=is_short,
) )
o = Order.parse_from_ccxt_object(mock_order_usdt_3(is_short), "XRP/USDT", entry_side(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, amount_requested=10.01,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=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, is_open=True,
open_rate=2.0, open_rate=2.0,
exchange="binance", exchange="binance",
@@ -280,7 +280,7 @@ def mock_trade_usdt_5(fee, is_short: bool):
amount_requested=10.01, amount_requested=10.01,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=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, is_open=True,
open_rate=2.0, open_rate=2.0,
exchange="binance", exchange="binance",
@@ -332,7 +332,7 @@ def mock_trade_usdt_6(fee, is_short: bool):
stake_amount=20.0, stake_amount=20.0,
amount=2.0, amount=2.0,
amount_requested=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_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
is_open=True, is_open=True,
@@ -374,7 +374,7 @@ def mock_trade_usdt_7(fee, is_short: bool):
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
is_open=True, 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, open_rate=2.0,
exchange="binance", exchange="binance",
strategy="StrategyTestV2", strategy="StrategyTestV2",
+15 -15
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock from unittest.mock import MagicMock
from zipfile import ZipFile from zipfile import ZipFile
@@ -182,19 +182,19 @@ def test_extract_trades_of_period(testdatadir):
"profit_abs": [0.0, 1, -2, -5], "profit_abs": [0.0, 1, -2, -5],
"open_date": to_datetime( "open_date": to_datetime(
[ [
datetime(2017, 11, 13, 15, 40, 0, tzinfo=timezone.utc), datetime(2017, 11, 13, 15, 40, 0, tzinfo=UTC),
datetime(2017, 11, 14, 9, 41, 0, tzinfo=timezone.utc), datetime(2017, 11, 14, 9, 41, 0, tzinfo=UTC),
datetime(2017, 11, 14, 14, 20, 0, tzinfo=timezone.utc), datetime(2017, 11, 14, 14, 20, 0, tzinfo=UTC),
datetime(2017, 11, 15, 3, 40, 0, tzinfo=timezone.utc), datetime(2017, 11, 15, 3, 40, 0, tzinfo=UTC),
], ],
utc=True, utc=True,
), ),
"close_date": to_datetime( "close_date": to_datetime(
[ [
datetime(2017, 11, 13, 16, 40, 0, tzinfo=timezone.utc), datetime(2017, 11, 13, 16, 40, 0, tzinfo=UTC),
datetime(2017, 11, 14, 10, 41, 0, tzinfo=timezone.utc), datetime(2017, 11, 14, 10, 41, 0, tzinfo=UTC),
datetime(2017, 11, 14, 15, 25, 0, tzinfo=timezone.utc), datetime(2017, 11, 14, 15, 25, 0, tzinfo=UTC),
datetime(2017, 11, 15, 3, 55, 0, tzinfo=timezone.utc), datetime(2017, 11, 15, 3, 55, 0, tzinfo=UTC),
], ],
utc=True, utc=True,
), ),
@@ -203,10 +203,10 @@ def test_extract_trades_of_period(testdatadir):
trades1 = extract_trades_of_period(data, trades) trades1 = extract_trades_of_period(data, trades)
# First and last trade are dropped as they are out of range # First and last trade are dropped as they are out of range
assert len(trades1) == 2 assert len(trades1) == 2
assert trades1.iloc[0].open_date == datetime(2017, 11, 14, 9, 41, 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=timezone.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=timezone.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=timezone.utc) assert trades1.iloc[-1].close_date == datetime(2017, 11, 14, 15, 25, 0, tzinfo=UTC)
def test_analyze_trade_parallelism(testdatadir): def test_analyze_trade_parallelism(testdatadir):
@@ -293,7 +293,7 @@ def test_combined_dataframes_with_rel_mean(testdatadir):
pairs = ["ETH/BTC", "ADA/BTC"] pairs = ["ETH/BTC", "ADA/BTC"]
data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m")
df = combined_dataframes_with_rel_mean( 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 isinstance(df, DataFrame)
assert "ETH/BTC" not in df.columns 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, 500, 1000, 11000, 10000] # absolute results
[1000, 50%, 0%, 0%, ~9%] # Relative drawdowns [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))] dates = [init_date + timedelta(days=i) for i in range(len(profits))]
df = DataFrame(zip(profits, dates, strict=False), columns=["profit_abs", "open_date"]) df = DataFrame(zip(profits, dates, strict=False), columns=["profit_abs", "open_date"])
# sort by profit and reset index # sort by profit and reset index
+8 -8
View File
@@ -1,7 +1,7 @@
# pragma pylint: disable=missing-docstring, protected-access, C0103 # pragma pylint: disable=missing-docstring, protected-access, C0103
import re import re
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock from unittest.mock import MagicMock
@@ -165,19 +165,19 @@ def test_datahandler_ohlcv_data_min_max(testdatadir):
# Empty pair # Empty pair
min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "8m", "spot") min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "8m", "spot")
assert len(min_max) == 3 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] assert min_max[0] == min_max[1]
# Empty pair2 # Empty pair2
min_max = dh.ohlcv_data_min_max("NOPAIR/XXX", "41m", "spot") min_max = dh.ohlcv_data_min_max("NOPAIR/XXX", "41m", "spot")
assert len(min_max) == 3 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] assert min_max[0] == min_max[1]
# Existing pair ... # Existing pair ...
min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "1m", "spot") min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "1m", "spot")
assert len(min_max) == 3 assert len(min_max) == 3
assert min_max[0] == datetime(2017, 11, 4, 23, 2, 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=timezone.utc) assert min_max[1] == datetime(2017, 11, 14, 22, 59, tzinfo=UTC)
def test_datahandler__check_empty_df(testdatadir, caplog): def test_datahandler__check_empty_df(testdatadir, caplog):
@@ -467,14 +467,14 @@ def test_datahandler_trades_data_min_max(testdatadir):
# Empty pair # Empty pair
min_max = dh.trades_data_min_max("NADA/ETH", TradingMode.SPOT) min_max = dh.trades_data_min_max("NADA/ETH", TradingMode.SPOT)
assert len(min_max) == 3 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] assert min_max[0] == min_max[1]
# Existing pair ... # Existing pair ...
min_max = dh.trades_data_min_max("XRP/ETH", TradingMode.SPOT) min_max = dh.trades_data_min_max("XRP/ETH", TradingMode.SPOT)
assert len(min_max) == 3 assert len(min_max) == 3
assert min_max[0] == datetime(2019, 10, 11, 0, 0, 11, 620000, 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=timezone.utc) assert min_max[1] == datetime(2019, 10, 13, 11, 19, 28, 844000, tzinfo=UTC)
def test_gethandlerclass(): def test_gethandlerclass():
+5 -5
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest import pytest
@@ -222,8 +222,8 @@ def test_get_producer_df(default_conf):
timeframe = default_conf["timeframe"] timeframe = default_conf["timeframe"]
candle_type = CandleType.SPOT candle_type = CandleType.SPOT
empty_la = datetime.fromtimestamp(0, tz=timezone.utc) empty_la = datetime.fromtimestamp(0, tz=UTC)
now = datetime.now(timezone.utc) now = datetime.now(UTC)
# no data has been added, any request should return an empty dataframe # no data has been added, any request should return an empty dataframe
dataframe, la = dataprovider.get_producer_df(pair, timeframe, candle_type) 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) dataframe, time = dp.get_analyzed_dataframe("NOTHING/BTC", timeframe)
assert dataframe.empty assert dataframe.empty
assert isinstance(time, datetime) assert isinstance(time, datetime)
assert time == datetime(1970, 1, 1, tzinfo=timezone.utc) assert time == datetime(1970, 1, 1, tzinfo=UTC)
# Test backtest mode # Test backtest mode
default_conf["runmode"] = RunMode.BACKTEST default_conf["runmode"] = RunMode.BACKTEST
@@ -478,7 +478,7 @@ def test_dp__add_external_df(default_conf_usdt):
default_conf_usdt["timeframe"] = timeframe default_conf_usdt["timeframe"] = timeframe
dp = DataProvider(default_conf_usdt, None) dp = DataProvider(default_conf_usdt, None)
df = generate_test_data(timeframe, 24, "2022-01-01 00:00:00+00:00") 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) res = dp._add_external_df("ETH/USDT", df, last_analyzed, timeframe, CandleType.SPOT)
assert res[0] is False assert res[0] is False
+10 -10
View File
@@ -1,6 +1,6 @@
# pragma pylint: disable=missing-docstring, C0103 # pragma pylint: disable=missing-docstring, C0103
from datetime import timezone from datetime import UTC
import pandas as pd import pandas as pd
from numpy import nan from numpy import nan
@@ -16,15 +16,15 @@ def test_get_tick_size_over_time():
# Create test dataframe with different levels of precision # Create test dataframe with different levels of precision
data = { data = {
"date": [ "date": [
Timestamp("2020-01-01 00:00:00", tz=timezone.utc), Timestamp("2020-01-01 00:00:00", tz=UTC),
Timestamp("2020-01-02 00:00:00", tz=timezone.utc), Timestamp("2020-01-02 00:00:00", tz=UTC),
Timestamp("2020-01-03 00:00:00", tz=timezone.utc), Timestamp("2020-01-03 00:00:00", tz=UTC),
Timestamp("2020-01-15 00:00:00", tz=timezone.utc), Timestamp("2020-01-15 00:00:00", tz=UTC),
Timestamp("2020-01-16 00:00:00", tz=timezone.utc), Timestamp("2020-01-16 00:00:00", tz=UTC),
Timestamp("2020-01-31 00:00:00", tz=timezone.utc), Timestamp("2020-01-31 00:00:00", tz=UTC),
Timestamp("2020-02-01 00:00:00", tz=timezone.utc), Timestamp("2020-02-01 00:00:00", tz=UTC),
Timestamp("2020-02-15 00:00:00", tz=timezone.utc), Timestamp("2020-02-15 00:00:00", tz=UTC),
Timestamp("2020-03-15 00:00:00", tz=timezone.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], "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], "high": [1.23457, 1.235, 1.24, 1.3, 1.23456, 1.235, 2.3457, 2.34, 2.34],
+3 -7
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from shutil import copytree from shutil import copytree
from unittest.mock import PropertyMock from unittest.mock import PropertyMock
@@ -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) trades = dh.trades_load("BCH_EUR", TradingMode.SPOT)
assert len(trades) == 340 assert len(trades) == 340
assert trades["date"].min().to_pydatetime() == datetime( assert trades["date"].min().to_pydatetime() == datetime(2023, 1, 1, 0, 3, 56, tzinfo=UTC)
2023, 1, 1, 0, 3, 56, tzinfo=timezone.utc assert trades["date"].max().to_pydatetime() == datetime(2023, 1, 2, 23, 17, 3, tzinfo=UTC)
)
assert trades["date"].max().to_pydatetime() == datetime(
2023, 1, 2, 23, 17, 3, tzinfo=timezone.utc
)
# ID is not filled # ID is not filled
assert len(trades.loc[trades["id"] != ""]) == 0 assert len(trades.loc[trades["id"] != ""]) == 0
+3 -3
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest 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): 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 = get_patched_exchange(mocker, default_conf, exchange="bybit")
exchange._fetch_and_calculate_funding_fees = MagicMock() exchange._fetch_and_calculate_funding_fees = MagicMock()
exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now) 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 return True
mocker.patch(f"{EXMS}.exchange_has", side_effect=exchange_has) 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") exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bybit")
# Not available in dry-run # Not available in dry-run
+16 -16
View File
@@ -1,7 +1,7 @@
import copy import copy
import logging import logging
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from random import randint from random import randint
from unittest.mock import MagicMock, Mock, PropertyMock, patch 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"]]) api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]])
mocker.patch(f"{EXMS}.exchange_has", return_value=True) 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 expected = 1
if exchange_name == "bybit": if exchange_name == "bybit":
expected = 3 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) exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
pair = "BTC/USDT" pair = "BTC/USDT"
candle_type = CandleType.SPOT 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) time_machine.move_to(start_dt, tick=False)
assert (pair, "5m", candle_type) not in exchange._pairs_last_refresh_time 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 assert exchange._now_is_time_to_refresh(pair, "5m", candle_type) is True
# Test with 1d data # 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)) 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 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): async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name, candle_type):
ohlcv = [ ohlcv = [
[ [
int((datetime.now(timezone.utc).timestamp() - 1000) * 1000), int((datetime.now(UTC).timestamp() - 1000) * 1000),
1, # open 1, # open
2, # high 2, # high
3, # low 3, # low
@@ -2501,7 +2501,7 @@ def test_refresh_latest_trades(
@pytest.mark.parametrize("candle_type", [CandleType.FUTURES, CandleType.MARK, CandleType.SPOT]) @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: 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")) ohlcv = generate_test_data_raw("1h", 100, start.strftime("%Y-%m-%d"))
time_machine.move_to(start + timedelta(hours=99, minutes=30)) 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: 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")) ohlcv = generate_test_data_raw("1h", 100, start.strftime("%Y-%m-%d"))
time_machine.move_to(start, tick=False) time_machine.move_to(start, tick=False)
pairs = [ pairs = [
@@ -2903,7 +2903,7 @@ def test_get_entry_rate(
mocker, default_conf, caplog, side, ask, bid, last, last_ab, expected, time_machine mocker, default_conf, caplog, side, ask, bid, last, last_ab, expected, time_machine
) -> None: ) -> None:
caplog.set_level(logging.DEBUG) 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) time_machine.move_to(start_dt, tick=False)
if last_ab is None: if last_ab is None:
del default_conf["entry_pricing"]["price_last_balance"] 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 default_conf, mocker, caplog, side, bid, ask, last, last_ab, expected, time_machine
) -> None: ) -> None:
caplog.set_level(logging.DEBUG) 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) time_machine.move_to(start_dt, tick=False)
default_conf["exit_pricing"]["price_side"] = side 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] == 1525478395000
assert ( assert (
api_mock.fetch_my_trades.call_args[0][1] 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( ccxt_exceptionhandlers(
@@ -4785,7 +4785,7 @@ def test_calculate_backoff(retrycount, max_retries, expected):
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_funding_fees(default_conf_usdt, mocker, exchange_name, caplog): 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["trading_mode"] = "futures"
default_conf_usdt["margin_mode"] = "isolated" default_conf_usdt["margin_mode"] = "isolated"
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange=exchange_name) 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) exchange = get_patched_exchange(mocker, default_conf)
kraken = get_patched_exchange(mocker, default_conf, exchange="kraken") kraken = get_patched_exchange(mocker, default_conf, exchange="kraken")
prior_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc) - timedelta(hours=1)) prior_date = timeframe_to_prev_date("1h", datetime.now(UTC) - timedelta(hours=1))
trade_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc)) trade_date = timeframe_to_prev_date("1h", datetime.now(UTC))
funding_rates = DataFrame( funding_rates = DataFrame(
[ [
{"date": prior_date, "open": funding_rate}, # Line not used. {"date": prior_date, "open": funding_rate}, # Line not used.
@@ -5072,9 +5072,9 @@ def test_combine_funding_and_mark(
futures_funding_rate, futures_funding_rate,
): ):
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
prior2_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc) - timedelta(hours=2)) prior2_date = timeframe_to_prev_date("1h", datetime.now(UTC) - timedelta(hours=2))
prior_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc) - timedelta(hours=1)) prior_date = timeframe_to_prev_date("1h", datetime.now(UTC) - timedelta(hours=1))
trade_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc)) trade_date = timeframe_to_prev_date("1h", datetime.now(UTC))
funding_rates = DataFrame( funding_rates = DataFrame(
[ [
{"date": prior2_date, "open": funding_rate}, {"date": prior2_date, "open": funding_rate},
+21 -21
View File
@@ -1,5 +1,5 @@
# pragma pylint: disable=missing-docstring, protected-access, invalid-name # 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 from math import isnan, nan
import pytest import pytest
@@ -117,7 +117,7 @@ def test_check_exchange(default_conf, caplog) -> None:
def test_date_minus_candles(): 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", 3, date) == date - timedelta(minutes=15)
assert date_minus_candles("5m", 5, date) == date - timedelta(minutes=25) 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(): def test_timeframe_to_prev_date():
# 2019-08-12 13:22:08 # 2019-08-12 13:22:08
date = datetime.fromtimestamp(1565616128, tz=timezone.utc) date = datetime.fromtimestamp(1565616128, tz=UTC)
tf_list = [ tf_list = [
# 5m -> 2019-08-12 13:20:00 # 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 -> 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 -> 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 -> 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 -> 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 -> 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: for interval, result in tf_list:
assert timeframe_to_prev_date(interval, date) == result 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 assert timeframe_to_prev_date("5m") < date
# Does not round # 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 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 assert timeframe_to_prev_date("1h", time) == time
def test_timeframe_to_next_date(): def test_timeframe_to_next_date():
# 2019-08-12 13:22:08 # 2019-08-12 13:22:08
date = datetime.fromtimestamp(1565616128, tz=timezone.utc) date = datetime.fromtimestamp(1565616128, tz=UTC)
tf_list = [ tf_list = [
# 5m -> 2019-08-12 13:25:00 # 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 -> 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 -> 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 -> 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 -> 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 -> 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: for interval, result in tf_list:
assert timeframe_to_next_date(interval, date) == result 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 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) assert timeframe_to_next_date("5m", date) == date + timedelta(minutes=5)
+12 -10
View File
@@ -74,14 +74,16 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog):
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
return MagicMock() 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.""" """Wait for a condition to be true with timeout."""
start_time = asyncio.get_event_loop().time() try:
while asyncio.get_event_loop().time() - start_time < timeout: async with asyncio.timeout(timeout_):
if condition_func(): while True:
return True if condition_func():
await asyncio.sleep(check_interval) return True
return False await asyncio.sleep(check_interval)
except TimeoutError:
return False
ccxt_object.un_watch_ohlcv_for_symbols = AsyncMock(side_effect=NotSupported) ccxt_object.un_watch_ohlcv_for_symbols = AsyncMock(side_effect=NotSupported)
ccxt_object.watch_ohlcv = AsyncMock(side_effect=controlled_sleeper) ccxt_object.watch_ohlcv = AsyncMock(side_effect=controlled_sleeper)
@@ -104,7 +106,7 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog):
lambda: ( lambda: (
len(exchange_ws._klines_watching) == 2 and len(exchange_ws._klines_scheduled) == 2 len(exchange_ws._klines_watching) == 2 and len(exchange_ws._klines_scheduled) == 2
), ),
timeout=2.0, timeout_=2.0,
) )
assert exchange_ws._klines_watching == { 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 # 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 assert ccxt_object.watch_ohlcv.call_count >= 6
ccxt_object.watch_ohlcv.reset_mock() ccxt_object.watch_ohlcv.reset_mock()
@@ -126,7 +128,7 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog):
# Wait for log message # Wait for log message
await wait_for_condition( 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) assert log_has_re("un_watch_ohlcv_for_symbols not supported: ", caplog)
+2 -2
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest 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 = get_patched_exchange(mocker, default_conf, api_mock=api_mock, exchange="gate")
exchange._trading_fees = tick 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] trade = trades[0]
assert trade["fee"] assert trade["fee"]
assert trade["fee"]["rate"] == rate assert trade["fee"]["rate"] == rate
+2 -2
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from unittest.mock import MagicMock, PropertyMock from unittest.mock import MagicMock, PropertyMock
import pytest 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): 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 = get_patched_exchange(mocker, default_conf, exchange="hyperliquid")
exchange._fetch_and_calculate_funding_fees = MagicMock() exchange._fetch_and_calculate_funding_fees = MagicMock()
exchange.get_funding_fees("BTC/USDC:USDC", 1, False, now) exchange.get_funding_fees("BTC/USDC:USDC", 1, False, now)
+6 -8
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from unittest.mock import AsyncMock, MagicMock, PropertyMock from unittest.mock import AsyncMock, MagicMock, PropertyMock
import ccxt import ccxt
@@ -15,7 +15,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers
def test_okx_ohlcv_candle_limit(default_conf, mocker): def test_okx_ohlcv_candle_limit(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, exchange="okx") exchange = get_patched_exchange(mocker, default_conf, exchange="okx")
timeframes = ("1m", "5m", "1h") 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: for timeframe in timeframes:
assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 300 assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 300
@@ -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 assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 100
one_call = int( one_call = int(
( (
datetime.now(timezone.utc) datetime.now(UTC) - timedelta(minutes=290 * timeframe_to_minutes(timeframe))
- timedelta(minutes=290 * timeframe_to_minutes(timeframe))
).timestamp() ).timestamp()
* 1000 * 1000
) )
@@ -40,8 +39,7 @@ def test_okx_ohlcv_candle_limit(default_conf, mocker):
one_call = int( one_call = int(
( (
datetime.now(timezone.utc) datetime.now(UTC) - timedelta(minutes=320 * timeframe_to_minutes(timeframe))
- timedelta(minutes=320 * timeframe_to_minutes(timeframe))
).timestamp() ).timestamp()
* 1000 * 1000
) )
@@ -693,7 +691,7 @@ def test_fetch_orders_okx(default_conf, mocker, limit_order):
api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]]) api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]])
mocker.patch(f"{EXMS}.exchange_has", return_value=True) 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") exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx")
# Not available in dry-run # Not available in dry-run
@@ -727,7 +725,7 @@ def test_fetch_orders_okx(default_conf, mocker, limit_order):
api_mock.fetch_closed_orders.reset_mock() api_mock.fetch_closed_orders.reset_mock()
# regular closed_orders endpoint only has history for 7 days. # 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_orders.call_count == 0
assert api_mock.fetch_open_orders.call_count == 1 assert api_mock.fetch_open_orders.call_count == 1
assert api_mock.fetch_closed_orders.call_count == 1 assert api_mock.fetch_closed_orders.call_count == 1
+8 -8
View File
@@ -5,7 +5,7 @@ However, these tests should give a good idea to determine if a new exchange is
suitable to run with freqtrade. suitable to run with freqtrade.
""" """
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
import pytest import pytest
@@ -248,7 +248,7 @@ class TestCCXTExchange:
len(exch.klines(pair_tf)) > exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) * 0.90 len(exch.klines(pair_tf)) > exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) * 0.90
) )
# Check if last-timeframe is within the last 2 intervals # 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).iloc[-1]["date"] >= timeframe_to_prev_date(timeframe, now)
def test_ccxt_fetch_ohlcv_startdate(self, exchange: EXCHANGE_FIXTURE_TYPE): def test_ccxt_fetch_ohlcv_startdate(self, exchange: EXCHANGE_FIXTURE_TYPE):
@@ -266,7 +266,7 @@ class TestCCXTExchange:
assert isinstance(ohlcv, dict) assert isinstance(ohlcv, dict)
assert len(ohlcv[pair_tf]) == len(exch.klines(pair_tf)) assert len(ohlcv[pair_tf]) == len(exch.klines(pair_tf))
# Check if last-timeframe is within the last 2 intervals # 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).iloc[-1]["date"] >= timeframe_to_prev_date(timeframe, now)
assert exch.klines(pair_tf)["date"].astype(int).iloc[0] // 1e6 == since_ms 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 self, exchange, exchangename, pair, timeframe, candle_type, factor=0.9
): ):
timeframe_ms = timeframe_to_msecs(timeframe) 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): for offset in (360, 120, 30, 10, 5, 2):
since = now - timedelta(days=offset) since = now - timedelta(days=offset)
since_ms = int(since.timestamp() * 1000) since_ms = int(since.timestamp() * 1000)
@@ -336,7 +336,7 @@ class TestCCXTExchange:
exchange, exchangename = exchange_futures exchange, exchangename = exchange_futures
pair = EXCHANGES[exchangename].get("futures_pair", EXCHANGES[exchangename]["pair"]) 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( timeframe_ff = exchange._ft_has.get(
"funding_fee_timeframe", exchange._ft_has["mark_ohlcv_timeframe"] "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): def test_ccxt_fetch_mark_price_history(self, exchange_futures: EXCHANGE_FIXTURE_TYPE):
exchange, exchangename = exchange_futures exchange, exchangename = exchange_futures
pair = EXCHANGES[exchangename].get("futures_pair", EXCHANGES[exchangename]["pair"]) 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) pair_tf = (pair, "1h", CandleType.MARK)
mark_ohlcv = exchange.refresh_latest_ohlcv([pair_tf], since_ms=since, drop_incomplete=False) 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): def test_ccxt__calculate_funding_fees(self, exchange_futures: EXCHANGE_FIXTURE_TYPE):
exchange, exchangename = exchange_futures exchange, exchangename = exchange_futures
pair = EXCHANGES[exchangename].get("futures_pair", EXCHANGES[exchangename]["pair"]) 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( funding_fee = exchange._fetch_and_calculate_funding_fees(
pair, 20, is_short=False, open_date=since pair, 20, is_short=False, open_date=since
@@ -403,7 +403,7 @@ class TestCCXTExchange:
if not (lookback := EXCHANGES[exchangename].get("trades_lookback_hours")): if not (lookback := EXCHANGES[exchangename].get("trades_lookback_hours")):
pytest.skip("test_fetch_trades not enabled for this exchange") pytest.skip("test_fetch_trades not enabled for this exchange")
pair = EXCHANGES[exchangename]["pair"] 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)) res = exch.loop.run_until_complete(exch._async_get_trade_history(pair, since, None, None))
assert len(res) == 2 assert len(res) == 2
res_pair, res_trades = res res_pair, res_trades = res
+6 -6
View File
@@ -1,5 +1,5 @@
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timezone from datetime import UTC, datetime
from pathlib import Path from pathlib import Path
from unittest.mock import PropertyMock 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): def test_freqai_backtest_start_backtest_list(freqai_conf, mocker, testdatadir, caplog):
patch_exchange(mocker) patch_exchange(mocker)
now = datetime.now(timezone.utc) now = datetime.now(UTC)
mocker.patch( mocker.patch(
"freqtrade.plugins.pairlistmanager.PairListManager.whitelist", "freqtrade.plugins.pairlistmanager.PairListManager.whitelist",
PropertyMock(return_value=["HULUMULU/USDT", "XRP/USDT"]), PropertyMock(return_value=["HULUMULU/USDT", "XRP/USDT"]),
@@ -73,7 +73,7 @@ def test_freqai_backtest_load_data(
): ):
patch_exchange(mocker) patch_exchange(mocker)
now = datetime.now(timezone.utc) now = datetime.now(UTC)
mocker.patch( mocker.patch(
"freqtrade.plugins.pairlistmanager.PairListManager.whitelist", "freqtrade.plugins.pairlistmanager.PairListManager.whitelist",
PropertyMock(return_value=["HULUMULU/USDT", "XRP/USDT"]), 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): def test_freqai_backtest_live_models_model_not_found(freqai_conf, mocker, testdatadir, caplog):
patch_exchange(mocker) patch_exchange(mocker)
now = datetime.now(timezone.utc) now = datetime.now(UTC)
mocker.patch( mocker.patch(
"freqtrade.plugins.pairlistmanager.PairListManager.whitelist", "freqtrade.plugins.pairlistmanager.PairListManager.whitelist",
PropertyMock(return_value=["HULUMULU/USDT", "XRP/USDT"]), 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 = Backtesting(deepcopy(freqai_conf))
backtesting.start() backtesting.start()
assert gbs.call_args[1]["min_date"] == datetime(2021, 11, 20, 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=timezone.utc) assert gbs.call_args[1]["max_date"] == datetime(2021, 11, 21, 0, 0, tzinfo=UTC)
Backtesting.cleanup() Backtesting.cleanup()
+3 -3
View File
@@ -1,5 +1,5 @@
import shutil import shutil
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock from unittest.mock import MagicMock
@@ -67,9 +67,9 @@ def test_split_timerange(
def test_check_if_model_expired(mocker, freqai_conf): def test_check_if_model_expired(mocker, freqai_conf):
dk = get_patched_data_kitchen(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 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 assert dk.check_if_model_expired(now) is True
shutil.rmtree(Path(dk.full_path)) shutil.rmtree(Path(dk.full_path))
+8 -8
View File
@@ -3,7 +3,7 @@
import random import random
from collections import defaultdict from collections import defaultdict
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
from unittest.mock import ANY, MagicMock, PropertyMock 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]) backtesting._set_strategy(backtesting.strategylist[0])
pair = "UNITTEST/BTC" pair = "UNITTEST/BTC"
row = [ 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 200, # Open
201.5, # High 201.5, # High
195, # Low 195, # Low
@@ -705,7 +705,7 @@ def test_backtest__check_trade_exit(default_conf, mocker) -> None:
assert isinstance(trade, LocalTrade) assert isinstance(trade, LocalTrade)
row_sell = [ 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 200, # Open
210.5, # High 210.5, # High
195, # Low 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()) res = backtesting._check_trade_exit(trade, row_sell, row_sell[0].to_pydatetime())
assert res is not None assert res is not None
assert res.exit_reason == ExitType.ROI.value 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 # Enter new trade
trade = backtesting._enter_trade(pair, row=row, direction="long") 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 assert len(t["orders"]) == 2
entryo = t["orders"][0] 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"]: if entry_ts > t["open_date"]:
late_entry += 1 late_entry += 1
@@ -1039,7 +1039,7 @@ def test_backtest_one_detail_futures(
assert len(t["orders"]) == 2 assert len(t["orders"]) == 2
entryo = t["orders"][0] 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"]: if entry_ts > t["open_date"]:
late_entry += 1 late_entry += 1
@@ -1121,7 +1121,7 @@ def test_backtest_one_detail_futures_funding_fees(
return df return df
def adjust_trade_position(trade, current_time, **kwargs): 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 None
return default_conf_usdt["stake_amount"] 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.Backtesting.backtest", backtestmock)
mocker.patch("freqtrade.optimize.backtesting.show_backtest_results", MagicMock()) 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) start_time = now - timedelta(**start_delta) + timedelta(hours=1)
if cache == "none": if cache == "none":
min_backtest_date = now + timedelta(days=1) min_backtest_date = now + timedelta(days=1)
+9 -9
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
import pytest import pytest
@@ -8,19 +8,19 @@ from tests.conftest import create_mock_trades_usdt
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_key_value_store(time_machine): 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) time_machine.move_to(start, tick=False)
KeyValueStore.store_value("test", "testStringValue") 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_float", 22.51)
KeyValueStore.store_value("test_int", 15) KeyValueStore.store_value("test_int", 15)
assert KeyValueStore.get_value("test") == "testStringValue" assert KeyValueStore.get_value("test") == "testStringValue"
assert KeyValueStore.get_value("test") == "testStringValue" assert KeyValueStore.get_value("test") == "testStringValue"
assert KeyValueStore.get_string_value("test") == "testStringValue" assert KeyValueStore.get_string_value("test") == "testStringValue"
assert KeyValueStore.get_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(timezone.utc) assert KeyValueStore.get_datetime_value("test_dt") == datetime.now(UTC)
assert KeyValueStore.get_string_value("test_dt") is None assert KeyValueStore.get_string_value("test_dt") is None
assert KeyValueStore.get_float_value("test_dt") is None assert KeyValueStore.get_float_value("test_dt") is None
assert KeyValueStore.get_int_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 assert KeyValueStore.get_datetime_value("test_int") is None
time_machine.move_to(start + timedelta(days=20, hours=5), tick=False) 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 assert KeyValueStore.get_value("test_dt") == start
# Test update works # Test update works
KeyValueStore.store_value("test_dt", datetime.now(timezone.utc)) KeyValueStore.store_value("test_dt", datetime.now(UTC))
assert KeyValueStore.get_value("test_dt") == datetime.now(timezone.utc) assert KeyValueStore.get_value("test_dt") == datetime.now(UTC)
KeyValueStore.store_value("test_float", 23.51) KeyValueStore.store_value("test_float", 23.51)
assert KeyValueStore.get_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") @pytest.mark.usefixtures("init_persistence")
def test_set_startup_time(fee, time_machine): def test_set_startup_time(fee, time_machine):
create_mock_trades_usdt(fee) create_mock_trades_usdt(fee)
start = datetime.now(timezone.utc) start = datetime.now(UTC)
time_machine.move_to(start, tick=False) time_machine.move_to(start, tick=False)
set_startup_time() set_startup_time()
+9 -9
View File
@@ -1,5 +1,5 @@
# pragma pylint: disable=missing-docstring, C0103 # pragma pylint: disable=missing-docstring, C0103
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from types import FunctionType from types import FunctionType
import pytest import pytest
@@ -265,7 +265,7 @@ def test_interest(fee, exchange, is_short, lev, minutes, rate, interest, trading
stake_amount=20.0, stake_amount=20.0,
amount=30.0, amount=30.0,
open_rate=2.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_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
exchange=exchange, exchange=exchange,
@@ -605,7 +605,7 @@ def test_calc_open_close_trade_price(
stake_amount=60.0, stake_amount=60.0,
open_rate=2.0, open_rate=2.0,
amount=30.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, interest_rate=0.0005,
fee_open=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value, fee_close=fee.return_value,
@@ -812,7 +812,7 @@ def test_calc_open_trade_value(
stake_amount=60.0, stake_amount=60.0,
amount=30.0, amount=30.0,
open_rate=2.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_open=fee_rate,
fee_close=fee_rate, fee_close=fee_rate,
exchange=exchange, exchange=exchange,
@@ -863,7 +863,7 @@ def test_calc_close_trade_price(
stake_amount=60.0, stake_amount=60.0,
amount=30.0, amount=30.0,
open_rate=open_rate, 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_open=fee_rate,
fee_close=fee_rate, fee_close=fee_rate,
exchange=exchange, exchange=exchange,
@@ -1164,7 +1164,7 @@ def test_calc_profit(
stake_amount=60.0, stake_amount=60.0,
amount=30.0 * lev, amount=30.0 * lev,
open_rate=2.0, 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, interest_rate=0.0005,
exchange=exchange, exchange=exchange,
is_short=is_short, is_short=is_short,
@@ -1882,7 +1882,7 @@ def test_get_trades_proxy(fee, use_db, is_short):
assert len(trades) == 2 assert len(trades) == 2
assert not trades[0].is_open 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 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") @pytest.mark.usefixtures("init_persistence")
def test_update_order_from_ccxt(caplog, time_machine): 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) time_machine.move_to(start, tick=False)
# Most basic order return (only has orderid) # Most basic order return (only has orderid)
@@ -2172,7 +2172,7 @@ def test_trade_truncates_string_fields():
stake_amount=20.0, stake_amount=20.0,
amount=30.0, amount=30.0,
open_rate=2.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_open=0.001,
fee_close=0.001, fee_close=0.001,
exchange="binance", exchange="binance",
+3 -3
View File
@@ -1,5 +1,5 @@
import json import json
from datetime import datetime, timezone from datetime import UTC, datetime
import pytest import pytest
@@ -182,7 +182,7 @@ def test_trade_fromjson():
assert trade.id == 25 assert trade.id == 25
assert trade.pair == "ETH/USDT" 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 isinstance(trade.open_date, datetime)
assert trade.exit_reason == "no longer good" assert trade.exit_reason == "no longer good"
assert trade.realized_profit == 2.76315361 assert trade.realized_profit == 2.76315361
@@ -192,7 +192,7 @@ def test_trade_fromjson():
assert len(trade.orders) == 5 assert len(trade.orders) == 5
last_o = trade.orders[-1] 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 isinstance(last_o.order_date, datetime)
assert last_o.funding_fee == -0.055 assert last_o.funding_fee == -0.055
+5 -5
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
import pytest import pytest
@@ -62,7 +62,7 @@ def test_PairLocks(use_db):
pair = "BTC/USDT" pair = "BTC/USDT"
# Lock until 14:30 # 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) PairLocks.lock_pair(pair, lock_time)
assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair)
@@ -121,15 +121,15 @@ def test_PairLocks_getlongestlock(use_db):
assert PairLocks.is_pair_locked(pair) assert PairLocks.is_pair_locked(pair)
lock = PairLocks.get_pair_longest_lock(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=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=14)
PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=15)) PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=15))
assert PairLocks.is_pair_locked(pair) assert PairLocks.is_pair_locked(pair)
lock = PairLocks.get_pair_longest_lock(pair) lock = PairLocks.get_pair_longest_lock(pair)
# Must be longer than above # 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.reset_locks()
PairLocks.use_db = True PairLocks.use_db = True
+4 -4
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pandas as pd 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, "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) time_machine.move_to(start, tick=False)
mock_ohlcv_data = { mock_ohlcv_data = {
@@ -224,7 +224,7 @@ def test_filter_pairlist_with_empty_ticker(mocker, rpl_config, tickers, time_mac
"lookback_days": 4, "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) time_machine.move_to(start, tick=False)
mock_ohlcv_data = { 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) time_machine.move_to(start, tick=False)
mock_ohlcv_data = { mock_ohlcv_data = {
+7 -7
View File
@@ -1,5 +1,5 @@
import random import random
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
import pytest import pytest
@@ -31,8 +31,8 @@ def generate_mock_trade(
stake_amount=0.01, stake_amount=0.01,
fee_open=fee, fee_open=fee,
fee_close=fee, fee_close=fee,
open_date=datetime.now(timezone.utc) - timedelta(minutes=min_ago_open or 200), open_date=datetime.now(UTC) - timedelta(minutes=min_ago_open or 200),
close_date=datetime.now(timezone.utc) - timedelta(minutes=min_ago_close or 30), close_date=datetime.now(UTC) - timedelta(minutes=min_ago_close or 30),
open_rate=open_rate, open_rate=open_rate,
is_open=is_open, is_open=is_open,
amount=0.01 / open_rate, amount=0.01 / open_rate,
@@ -99,9 +99,9 @@ def test_protectionmanager(mocker, default_conf):
for handler in freqtrade.protections._protection_handlers: for handler in freqtrade.protections._protection_handlers:
assert handler.name in AVAILABLE_PROTECTIONS assert handler.name in AVAILABLE_PROTECTIONS
if not handler.has_global_stop: 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: 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( @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) assert not log_has_re(message, caplog)
caplog.clear() 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) time_machine.move_to(start_dt, tick=False)
generate_mock_trade( 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() assert not PairLocks.is_global_lock()
# Force rollover to the next day. # 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) time_machine.move_to(start_dt, tick=False)
generate_mock_trade( generate_mock_trade(
"ETH/BTC", "ETH/BTC",
+5 -5
View File
@@ -1,5 +1,5 @@
from copy import deepcopy from copy import deepcopy
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from unittest.mock import ANY, MagicMock, PropertyMock from unittest.mock import ANY, MagicMock, PropertyMock
import pytest 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["starting_balance"] in (pytest.approx(1062.37), pytest.approx(1066.46))
assert day["fiat_value"] in (0.0,) assert day["fiat_value"] in (0.0,)
# ensure first day is current date # 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 # Try invalid data
with pytest.raises(RPCException, match=r".*must be an integer greater than 0*"): 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) rpc = RPC(freqtradebot)
pair = "ETH/BTC" pair = "ETH/BTC"
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=4), "", "*") rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=4), "", "*")
rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=5), "", "*") rpc._rpc_add_lock(pair, datetime.now(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=10), "", "*")
locks = rpc._rpc_locks() locks = rpc._rpc_locks()
assert locks["lock_count"] == 3 assert locks["lock_count"] == 3
+5 -5
View File
@@ -5,7 +5,7 @@ Unit test file for rpc/api_server.py
import asyncio import asyncio
import logging import logging
import time import time
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
from unittest.mock import ANY, MagicMock, PropertyMock from unittest.mock import ANY, MagicMock, PropertyMock
@@ -637,12 +637,12 @@ def test_api_locks(botclient):
[ [
{ {
"pair": "ETH/BTC", "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", "reason": "randreason",
}, },
{ {
"pair": "XRP/BTC", "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", "reason": "deadbeef",
}, },
], ],
@@ -711,7 +711,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
assert len(response["data"]) == 7 assert len(response["data"]) == 7
assert response["stake_currency"] == "BTC" assert response["stake_currency"] == "BTC"
assert response["fiat_display_currency"] == "USD" 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): 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", exchange="binance",
stake_amount=1, stake_amount=1,
open_rate=0.245441, open_rate=0.245441,
open_date=datetime.now(timezone.utc), open_date=datetime.now(UTC),
is_open=False, is_open=False,
is_short=False, is_short=False,
fee_close=fee.return_value, fee_close=fee.return_value,
+4 -4
View File
@@ -4,7 +4,7 @@ Unit test file for rpc/external_message_consumer.py
import asyncio import asyncio
import logging import logging
from datetime import datetime, timezone from datetime import UTC, datetime
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest import pytest
@@ -99,7 +99,7 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history):
"data": { "data": {
"key": ("BTC/USDT", "5m", "spot"), "key": ("BTC/USDT", "5m", "spot"),
"df": ohlcv_history, "df": ohlcv_history,
"la": datetime.now(timezone.utc), "la": datetime.now(UTC),
}, },
} }
patched_emc.handle_producer_message(test_producer, df_message) 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 = { malformed_message = {
"type": "analyzed_df", "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) 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": { "data": {
"key": ("BTC/USDT", "5m", "spot"), "key": ("BTC/USDT", "5m", "spot"),
"df": ohlcv_history.loc[ohlcv_history["open"] < 0], "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) patched_emc.handle_producer_message(test_producer, malformed_message)
+3 -3
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timezone from datetime import UTC, datetime
import pytest import pytest
from pandas import DataFrame from pandas import DataFrame
@@ -47,7 +47,7 @@ def test_strategy_test_v3(dataframe_1m, fee, is_short, side):
amount=0.1, amount=0.1,
rate=20000, rate=20000,
time_in_force="gtc", time_in_force="gtc",
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
side=side, side=side,
entry_tag=None, entry_tag=None,
) )
@@ -63,7 +63,7 @@ def test_strategy_test_v3(dataframe_1m, fee, is_short, side):
time_in_force="gtc", time_in_force="gtc",
exit_reason="roi", exit_reason="roi",
sell_reason="roi", sell_reason="roi",
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
side=side, side=side,
) )
is True is True
+5 -5
View File
@@ -1,7 +1,7 @@
# pragma pylint: disable=missing-docstring, C0103 # pragma pylint: disable=missing-docstring, C0103
import logging import logging
import math import math
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock from unittest.mock import MagicMock
@@ -208,7 +208,7 @@ def test_ignore_expired_candle(default_conf):
strategy = StrategyResolver.load_strategy(default_conf) strategy = StrategyResolver.load_strategy(default_conf)
strategy.ignore_buying_expired_candle_after = 60 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. # Add 1 candle length as the "latest date" defines candle open.
current_time = latest_date + timedelta(seconds=80 + 300) current_time = latest_date + timedelta(seconds=80 + 300)
@@ -765,7 +765,7 @@ def test_leverage_callback(default_conf, side) -> None:
assert ( assert (
strategy.leverage( strategy.leverage(
pair="XRP/USDT", pair="XRP/USDT",
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
current_rate=2.2, current_rate=2.2,
proposed_leverage=1.0, proposed_leverage=1.0,
max_leverage=5.0, max_leverage=5.0,
@@ -780,7 +780,7 @@ def test_leverage_callback(default_conf, side) -> None:
assert ( assert (
strategy.leverage( strategy.leverage(
pair="XRP/USDT", pair="XRP/USDT",
current_time=datetime.now(timezone.utc), current_time=datetime.now(UTC),
current_rate=2.2, current_rate=2.2,
proposed_leverage=1.0, proposed_leverage=1.0,
max_leverage=5.0, max_leverage=5.0,
@@ -897,7 +897,7 @@ def test_is_pair_locked(default_conf):
pair = "BTC/USDT" pair = "BTC/USDT"
# Lock until 14:30 # 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. # Subtract 2 seconds, as locking rounds up to the next candle.
strategy.lock_pair(pair, lock_time - timedelta(seconds=2)) strategy.lock_pair(pair, lock_time - timedelta(seconds=2))
+4 -4
View File
@@ -1,5 +1,5 @@
# pragma pylint: disable=missing-docstring, C0103 # pragma pylint: disable=missing-docstring, C0103
from datetime import datetime, timezone from datetime import UTC, datetime
import pytest import pytest
@@ -29,8 +29,8 @@ def test_parse_timerange_incorrect():
assert TimeRange("date", "date", 1231006505, 1233360000) == timerange assert TimeRange("date", "date", 1231006505, 1233360000) == timerange
assert isinstance(timerange.startdt, datetime) assert isinstance(timerange.startdt, datetime)
assert isinstance(timerange.stopdt, datetime) assert isinstance(timerange.stopdt, datetime)
assert timerange.startdt == datetime.fromtimestamp(1231006505, tz=timezone.utc) assert timerange.startdt == datetime.fromtimestamp(1231006505, tz=UTC)
assert timerange.stopdt == datetime.fromtimestamp(1233360000, tz=timezone.utc) assert timerange.stopdt == datetime.fromtimestamp(1233360000, tz=UTC)
assert timerange.timerange_str == "20090103-20090131" assert timerange.timerange_str == "20090103-20090131"
timerange = TimeRange.parse_timerange("1231006505000-1233360000000") timerange = TimeRange.parse_timerange("1231006505000-1233360000000")
@@ -68,7 +68,7 @@ def test_subtract_start():
def test_adjust_start_if_necessary(): 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) x = TimeRange("date", "date", 1510694100, 1510780500)
# Adjust by 20 candles - min_date == startts # Adjust by 20 candles - min_date == startts
+22 -26
View File
@@ -1,4 +1,4 @@
from datetime import datetime, timedelta, timezone from datetime import UTC, datetime, timedelta
import pytest import pytest
import time_machine import time_machine
@@ -21,14 +21,14 @@ from freqtrade.util.datetime_helpers import dt_humanize_delta
def test_dt_now(): def test_dt_now():
with time_machine.travel("2021-09-01 05:01:00 +00:00", tick=False) as t: 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_now() == now
assert dt_ts() == int(now.timestamp() * 1000) assert dt_ts() == int(now.timestamp() * 1000)
assert dt_ts(now) == int(now.timestamp() * 1000) assert dt_ts(now) == int(now.timestamp() * 1000)
t.shift(timedelta(hours=5)) t.shift(timedelta(hours=5))
assert dt_now() >= now 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) assert dt_ts() == int(dt_now().timestamp() * 1000)
# Test with different time than now # Test with different time than now
assert dt_ts(now) == int(now.timestamp() * 1000) assert dt_ts(now) == int(now.timestamp() * 1000)
@@ -37,42 +37,38 @@ def test_dt_now():
def test_dt_ts_def(): def test_dt_ts_def():
assert dt_ts_def(None) == 0 assert dt_ts_def(None) == 0
assert dt_ts_def(None, 123) == 123 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=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), 123) == 1683244800000
def test_dt_ts_none(): def test_dt_ts_none():
assert dt_ts_none(None) is None assert dt_ts_none(None) is 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=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
def test_dt_utc(): 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( assert dt_utc(2023, 5, 5, 0, 0, 0, 555500) == datetime(2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC)
2023, 5, 5, 0, 0, 0, 555500, tzinfo=timezone.utc
)
@pytest.mark.parametrize("as_ms", [True, False]) @pytest.mark.parametrize("as_ms", [True, False])
def test_dt_from_ts(as_ms): def test_dt_from_ts(as_ms):
multi = 1000 if as_ms else 1 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( assert dt_from_ts(1683244800.5555 * multi) == datetime(2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC)
2023, 5, 5, 0, 0, 0, 555500, tzinfo=timezone.utc
)
# As int # 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 # As milliseconds
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)
assert dt_from_ts(1683242400 * multi) == datetime(2023, 5, 4, 23, 20, tzinfo=timezone.utc) assert dt_from_ts(1683242400 * multi) == datetime(2023, 5, 4, 23, 20, tzinfo=UTC)
def test_dt_floor_day(): 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: def test_shorten_date() -> None:
@@ -95,21 +91,21 @@ def test_format_ms_time() -> None:
date_in_epoch_ms = 1523383321132 date_in_epoch_ms = 1523383321132
date = format_ms_time(date_in_epoch_ms) date = format_ms_time(date_in_epoch_ms)
assert isinstance(date, str) 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 == res.strftime("%Y-%m-%dT%H:%M:%S")
assert date == "2018-04-10T18:02:01" 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 2017-12-13 08:02:01
date_in_epoch_ms = 1513152121000 date_in_epoch_ms = 1513152121000
assert format_ms_time(date_in_epoch_ms) == res.strftime("%Y-%m-%dT%H:%M:%S") assert format_ms_time(date_in_epoch_ms) == res.strftime("%Y-%m-%dT%H:%M:%S")
def test_format_date() -> None: 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(date) == "2023-09-01 05:02:03"
assert format_date(None) == "" 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(date) == "2021-09-30 22:59:03"
assert format_date(None) == "" assert format_date(None) == ""
@@ -119,10 +115,10 @@ def test_format_ms_time_detailed() -> None:
date_in_epoch_ms = 1523383321132 date_in_epoch_ms = 1523383321132
date = format_ms_time_det(date_in_epoch_ms) date = format_ms_time_det(date_in_epoch_ms)
assert isinstance(date, str) 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 == res.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
assert date == "2018-04-10T18:02:01.132" 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 2017-12-13 08:02:01
date_in_epoch_ms = 1513152121512 date_in_epoch_ms = 1513152121512
assert format_ms_time_det(date_in_epoch_ms) == res.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] assert format_ms_time_det(date_in_epoch_ms) == res.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]