Source code for trader.reporting.window

"""Shared default-start-date resolution for report commands (issue #57).

`docs/operational-timeline.md` (2026-08-22) established that the live account
has no single clean "went live" instant — 2026-07-30 through 2026-08-16 mixes
manual `run-once` testing, an intermittent early daemon, a real business-trip
outage, and two machine migrations. Every report command used to default to
the earliest data available, which silently folded that pre-service noise
into every headline number unless an operator remembered to pass `--start` by
hand. `config/reporting.yaml`'s optional `default_report_start_date` fixes the
default; this module is the ONE place that decides how it interacts with an
explicit `--start` and the new `--since-creation` opt-out, so five call sites
(`trader performance`, `trader outcomes`, `trader sim-report`,
`tools/roi_investigation.py`, `trader web`) cannot each invent slightly
different precedence rules.
"""

from datetime import UTC, date, datetime, time

__all__ = ["resolve_report_start"]


[docs] def resolve_report_start( *, explicit_start: datetime | None, since_creation: bool, default_start_date: date | None, ) -> datetime | None: """The effective start-of-window `datetime` a report command should use. Precedence, matching the issue's own wording and every affected command's docstring: 1. `explicit_start` (a real `--start` the operator typed) ALWAYS wins, even over `--since-creation` — the flag combination "give me full history, but not before this date" only makes sense if `--start` stays authoritative when both are given. 2. `since_creation=True` returns `None` — no floor, full history — the opt-out this issue adds. Only reached when `explicit_start` is absent. 3. `default_start_date` (`ReportingConfig.default_report_start_date`), converted to UTC midnight — the new default this issue ships. 4. `default_start_date is None` (the config field was never set) returns `None` — full history, byte-for-byte the pre-issue-#57 default. This is the "absence is fine, wrongness is not" rule `config/reporting.yaml` already documents for `benchmark_tickers`, extended to this field. `None` throughout this module means "no floor" — the same convention `SnapshotRepository.snapshots_in_range`/`snapshots_since` already use, so a caller can pass this straight through to those methods (or an equivalent date-range filter) unchanged. """ if explicit_start is not None: return explicit_start if since_creation: return None if default_start_date is not None: return datetime.combine(default_start_date, time.min, tzinfo=UTC) return None