chart/app/bars/session.py

30 lines
1.2 KiB
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:
if tf not in (Timeframe.H4, Timeframe.D1):
return (t // tf.seconds) * tf.seconds
current = datetime.fromtimestamp(t, UTC).astimezone(EASTERN)
session_open = _session_open_local(current)
if tf is Timeframe.D1:
return int(session_open.timestamp())
# CME's 4h anchors are wall-clock ET anchors. This intentionally makes the
# DST-transition bucket three or five elapsed hours instead of shifting it.
elapsed_wall = current.replace(tzinfo=None) - session_open.replace(tzinfo=None)
bucket_hours = int(elapsed_wall.total_seconds() // 14400) * 4
local_start = session_open.replace(tzinfo=None) + timedelta(hours=bucket_hours)
return int(local_start.replace(tzinfo=EASTERN).timestamp())