Source code for tradingstrategy.utils.time
"""Helpers to timestamp format and value conformity.
- We are operating on naive Python datetimes, all in UTC timezone
"""
import calendar
import datetime
import pandas as pd
#: Pre-instiated no difference pd.Tiemdelta for optimisation
ZERO_TIMEDELTA = pd.Timedelta(0)
[docs]def is_compatible_timestamp(ts: pd.Timestamp) -> bool:
"""Ensure Pandas Timestamp is naive.
We do not carry timezone information in data, because it would slow
us down.
"""
assert isinstance(ts, pd.Timestamp), f"We assume pandas.Timestamp, but received {ts}"
return ts.tz is None
[docs]def assert_compatible_timestamp(ts: pd.Timestamp):
"""Check we do not get in bad input timestamps.
:raise: AssertionError if the timestamp object is incompatible
"""
assert isinstance(ts, pd.Timestamp), f"not pd.Timestamp: {ts.__class__.__name__}={ts}"
assert is_compatible_timestamp(ts), f"Received pandas.Timestamp in incompatible format: {type(ts)}: {ts}"
[docs]def to_int_unix_timestamp(dt: datetime.datetime) -> int:
"""Get datetime as UTC seconds since epoch."""
# https://stackoverflow.com/a/5499906/315168
return int(calendar.timegm(dt.utctimetuple()))
[docs]def generate_monthly_timestamps(start: datetime.datetime, end: datetime.datetime) -> list[int]:
"""Generate timestamps from the start to the end. One timestamp per month.
:param start: Start date
:param end: End date
:return: List of timestamps
"""
# TODO: ensure index has no missing dates i.e. evenly spaced intervals throughout the period
timestamps = []
current_date = start
while current_date <= end:
timestamps.append(int(current_date.timestamp()))
# Check if adding one month stays within the year
if current_date.month == 12:
current_date = current_date.replace(year=current_date.year + 1, month=1)
else:
current_date = current_date.replace(month=current_date.month + 1)
if current_date > end:
timestamps.append(int(end.timestamp()))
break
return timestamps
[docs]def naive_utcnow() -> datetime.datetime:
"""Get utcnow() but without timezone information.
Fixes for Python 3.12 compatibility
- https://blog.miguelgrinberg.com/post/it-s-time-for-a-change-datetime-utcnow-is-now-deprecated
"""
return datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
[docs]def naive_utcfromtimestamp(timestamp: float | int) -> datetime.datetime:
"""Get naive UTC datetime from UNIX time.
Fixes for Python 3.12 compatibility
- https://blog.miguelgrinberg.com/post/it-s-time-for-a-change-datetime-utcnow-is-now-deprecated
"""
return datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc).replace(tzinfo=None)
[docs]def get_prior_timestamp(series: pd.Series, ts: pd.Timestamp) -> pd.Timestamp | None:
"""Get the first timestamp in the index that is before the given timestamp.
:return:
Any timestamp from the index that is before or at the same time of the given timestamp.
Return ``None`` if there are no earlier timestamps.
"""
index = series.index
# The original data is in grouped DF
if isinstance(index, pd.MultiIndex):
# AssertionError: Got index: MultiIndex([(2854997, '2024-04-04 21:00:00'),
# (2854997, '2024-04-04 22:00:00'),
index = index.get_level_values(1)
assert isinstance(index, pd.DatetimeIndex), f"Got index: {index}"
try:
return index[index < ts][-1]
except IndexError:
return None
[docs]def floor_pandas_week(ts: pd.Timestamp) -> pd.Timestamp:
"""Round Pandas timestamp to a start of a week."""
return ts.to_period("W").start_time
[docs]def floor_pandas_month(ts: pd.Timestamp) -> pd.Timestamp:
"""Round Pandas timestamp to a start of a month."""
return ts.to_period("M").start_time
[docs]def to_unix_timestamp(dt: datetime.datetime) -> float:
"""Convert Python UTC datetime to UNIX seconds since epoch.
Example:
.. code-block:: python
import datetime
from eth_defi.utils import to_unix_timestamp
dt = datetime.datetime(1970, 1, 1)
unix_time = to_unix_timestamp(dt)
assert unix_time == 0
:param dt:
Python datetime to convert
:return:
Datetime as seconds since 1970-1-1
"""
# https://stackoverflow.com/a/5499906/315168
return calendar.timegm(dt.utctimetuple())