"""Unit tests for libs/backtest/calendar.py.""" from __future__ import annotations import datetime as dt import pytest from libs.backtest.calendar import ( get_trading_days, is_trading_day, next_trading_day, resolve_execution_date, ) class TestResolvExecutionDate: def test_pre_market_weekday(self): # pre_market on a trading day: reaction = same day, execution = next trading day event_date = dt.date(2026, 1, 5) # Monday exec_date = resolve_execution_date(event_date, "pre_market") assert is_trading_day(exec_date) assert exec_date > event_date def test_post_market_weekday(self): # post_market: reaction = next trading day, execution = trading day after that event_date = dt.date(2026, 1, 5) # Monday exec_date = resolve_execution_date(event_date, "post_market") assert is_trading_day(exec_date) assert exec_date > event_date def test_post_market_friday(self): # post_market Friday → reaction = Monday, execution = Tuesday friday = dt.date(2026, 1, 2) # Friday exec_date = resolve_execution_date(friday, "post_market") assert is_trading_day(exec_date) # Must be at least Monday assert exec_date >= dt.date(2026, 1, 5) def test_unknown_bucket(self): # unknown treated same as post_market event_date = dt.date(2026, 1, 5) exec_date = resolve_execution_date(event_date, "unknown") assert is_trading_day(exec_date) assert exec_date > event_date def test_execution_after_reaction(self): """execution_date should always be strictly after event_date.""" for bucket in ["pre_market", "regular_hours", "post_market", "unknown"]: exec_date = resolve_execution_date(dt.date(2026, 1, 5), bucket) assert exec_date > dt.date(2026, 1, 5), f"Failed for bucket: {bucket}" class TestGetTradingDays: def test_basic_range(self): days = get_trading_days(dt.date(2026, 1, 5), dt.date(2026, 1, 9)) assert len(days) == 5 # Mon-Fri assert all(is_trading_day(d) for d in days) def test_excludes_weekends(self): days = get_trading_days(dt.date(2026, 1, 3), dt.date(2026, 1, 11)) for d in days: assert d.weekday() < 5 # Not Saturday (5) or Sunday (6) def test_single_day(self): days = get_trading_days(dt.date(2026, 1, 5), dt.date(2026, 1, 5)) assert len(days) == 1 assert days[0] == dt.date(2026, 1, 5) def test_sorted_ascending(self): days = get_trading_days(dt.date(2026, 1, 5), dt.date(2026, 1, 30)) assert days == sorted(days) class TestIsTradingDay: def test_weekday_is_trading(self): assert is_trading_day(dt.date(2026, 1, 5)) # Monday def test_weekend_not_trading(self): assert not is_trading_day(dt.date(2026, 1, 3)) # Saturday def test_sunday_not_trading(self): assert not is_trading_day(dt.date(2026, 1, 4)) # Sunday class TestNextTradingDay: def test_friday_to_monday(self): friday = dt.date(2026, 1, 2) nxt = next_trading_day(friday) assert nxt == dt.date(2026, 1, 5) # Monday def test_monday_to_tuesday(self): monday = dt.date(2026, 1, 5) nxt = next_trading_day(monday) assert nxt == dt.date(2026, 1, 6)