You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
3.3 KiB
Python
95 lines
3.3 KiB
Python
"""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)
|