Source code for trader.replay.sessions
"""Trading sessions, as the app's own bars define them.
The horizon in issue #7's scoring rule is counted in *trading days*, off the
distinct `1d` bar dates this app already holds — never off a weekday rule,
which is wrong at every market holiday and would silently shorten the
horizon.
"""
from collections.abc import Sequence
from datetime import date, datetime
from zoneinfo import ZoneInfo
from trader.domain import Bar
__all__ = ["session_grid", "session_offset", "trading_date"]
#: Stored `1d` bars are stamped at *exchange* midnight, which is 04:00Z in
#: summer and 05:00Z in winter. A fixed UTC offset is therefore wrong for four
#: months a year, and `stamp.date()` in UTC moves a summer bar to the previous
#: day. `tools/buy_and_hold_benchmark.py` records the same reasoning.
_EXCHANGE_TZ = ZoneInfo("America/New_York")
[docs]
def trading_date(stamp: datetime) -> date:
"""Which trading day a bar timestamp (or decision instant) names.
Raises:
ValueError: `stamp` is naive. Guessing the zone would move the bar by
a day for four months of the year, which is exactly the error
this function exists to prevent.
"""
if stamp.tzinfo is None:
raise ValueError(f"trading_date needs a tz-aware datetime, got {stamp!r}")
return stamp.astimezone(_EXCHANGE_TZ).date()
[docs]
def session_grid(bars: Sequence[Bar]) -> list[date]:
"""The sorted, deduplicated trading days these bars cover."""
return sorted({trading_date(b.timestamp) for b in bars})
[docs]
def session_offset(grid: Sequence[date], start: date, n: int) -> date | None:
"""The session `n` places after `start` on `grid`, or `None` past the end.
`None` rather than the last available day: clamping would quietly shorten
the horizon for rows near the end of the window and inflate every long-N
result. The caller drops the row instead.
Raises:
ValueError: `start` is not on the grid, which means the caller built
a decision date the bars do not support.
"""
try:
index = grid.index(start)
except ValueError as exc:
raise ValueError(f"{start} is not a trading session on this grid") from exc
target = index + n
return grid[target] if target < len(grid) else None