It was never in the enabled timeframes, so it held zero bars and produced no levels, but it still carried weight 8 in the scoring table and forced bucket_start to special-case a wall-clock ET anchor whose entire purpose was surviving DST transitions. That was the most intricate logic in session.py, maintained for a timeframe nobody used. Daily is now the only session-anchored bucket, which is a much easier rule to state and to keep correct. The DST parametrised tests go with it; the Sunday open and daily boundary cases remain. Manual-line tests move to 1h, so the weight assertions drop from 8 to 4. The plan document keeps its 4h examples — rewriting a dozen illustrative sentences would churn more than it clarifies — but the timeframe-roles section now records the removal so nothing reads as a spec to build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
24 lines
925 B
Python
24 lines
925 B
Python
from datetime import datetime, time, timedelta
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from app.bars.models import Timeframe
|
|
|
|
UTC = ZoneInfo("UTC")
|
|
EASTERN = ZoneInfo("America/New_York")
|
|
SESSION_OPEN = time(18, 0)
|
|
|
|
|
|
def _session_open_local(current: datetime) -> datetime:
|
|
session_date = current.date() if current.timetz().replace(tzinfo=None) >= SESSION_OPEN else current.date() - timedelta(days=1)
|
|
return datetime.combine(session_date, SESSION_OPEN, EASTERN)
|
|
|
|
|
|
def bucket_start(t: int, tf: Timeframe) -> int:
|
|
# Everything below a day divides the hour evenly, so UTC boundaries are
|
|
# correct and session anchoring is unnecessary. Only the daily bar needs to
|
|
# know that the CME trading day runs 18:00 to 17:00 ET.
|
|
if tf is not Timeframe.D1:
|
|
return (t // tf.seconds) * tf.seconds
|
|
|
|
current = datetime.fromtimestamp(t, UTC).astimezone(EASTERN)
|
|
return int(_session_open_local(current).timestamp())
|