"""Compute the reaction date for a filing based on its time bucket.""" from __future__ import annotations import datetime as dt from libs.common.time_utils import is_trading_day def _advance_to_next_trading_day(date: dt.date) -> dt.date: """Return the next calendar day that is a trading day (starting from date+1).""" check = date + dt.timedelta(days=1) for _ in range(30): # guard against infinite loop if is_trading_day(check): return check check += dt.timedelta(days=1) raise RuntimeError(f"Could not find trading day within 30 days of {date}") def _to_trading_day_on_or_after(date: dt.date) -> dt.date: """Return date itself if a trading day, else the next trading day.""" for _ in range(30): if is_trading_day(date): return date date += dt.timedelta(days=1) raise RuntimeError("Could not find trading day within 30 days") def compute_reaction_date( event_date: dt.date, filing_time_bucket: str, ) -> dt.date: """Return the first trading day on which the market can react to the filing. Rules: - pre_market / regular_hours → same day if it is a trading day, else next. - post_market / unknown → next trading day after event_date. Args: event_date: The calendar date of the filing. filing_time_bucket: One of pre_market, regular_hours, post_market, unknown. Returns: The reaction date (a trading day). """ if filing_time_bucket in ("pre_market", "regular_hours"): return _to_trading_day_on_or_after(event_date) else: # post_market or unknown: market reacts next trading day return _advance_to_next_trading_day(event_date)