from app.analysis.levels import Level, LevelKind, Side from app.bars.models import Bar, Timeframe from app.config import TIMEFRAME_WEIGHT PRIOR_DAY_SPECS = (("high", "PDH", "Prior day high"), ("low", "PDL", "Prior day low"), ("close", "PDC", "Prior day close")) def build_prior_day_levels(daily_bars: list[Bar], current_price: float | None) -> list[Level]: """Prior session high, low and close. The newest daily bar is normally still forming, so "prior day" means the last *closed* session. Taking the last bar outright would silently switch the levels to today's own developing range partway through the session, which is not what anyone means by PDH. These carry the full daily weight rather than the moving-average discount: an actual prior high is traded structure, not a derived average. """ closed = [bar for bar in daily_bars if bar.closed] if not closed: return [] prior = closed[-1] reference = current_price if current_price is not None else prior.c prices = {"high": prior.h, "low": prior.l, "close": prior.c} return [ Level( id=f"pd:{key}", kind=LevelKind.HORIZONTAL, tf=Timeframe.D1, side=Side.SUPPORT if prices[key] <= reference else Side.RESISTANCE, weight=TIMEFRAME_WEIGHT[Timeframe.D1], score=1.0, label=short, anchor_t=prior.t, anchor_p=prices[key], slope=0.0, points=None, touches=0, first_t=prior.t, last_t=prior.t, provisional=False, hidden=False, ) for key, short, _description in PRIOR_DAY_SPECS ]