diff --git a/tests/test_snapshot_backfill.py b/tests/test_snapshot_backfill.py index ecf3ec57..3d625b01 100644 --- a/tests/test_snapshot_backfill.py +++ b/tests/test_snapshot_backfill.py @@ -210,11 +210,31 @@ def test_verifier_reports_mismatch(self, monkeypatch): """검증 도구가 불일치를 실제로 잡아내는가(달력이 맞아도 로직은 살아 있어야).""" import tools.verify_trading_calendar as v - # 시장은 6/1만 열렸다고 가정 — 나머지 평일은 전부 wrong_open이어야 한다 - monkeypatch.setattr(v, "market_open_days", lambda s, e, symbol=None: {date(2026, 6, 1)}) - r = v.compare(date(2026, 6, 1), date(2026, 6, 5)) - assert date(2026, 6, 1) not in r["wrong_open"] - assert date(2026, 6, 2) in r["wrong_open"] + # 6/8·6/10만 열렸다고 가정하면 그 사이 평일 6/9가 wrong_open으로 잡혀야 한다. + # (6/3은 실제 휴장일로 등록돼 있어 대조 대상에서 제외 — 순수 로직만 본다) + monkeypatch.setattr( + v, "market_open_days", + lambda s, e, symbol=None: {date(2026, 6, 8), date(2026, 6, 10)}, + ) + r = v.compare(date(2026, 6, 8), date(2026, 6, 12)) + assert date(2026, 6, 9) in r["wrong_open"] + assert date(2026, 6, 8) not in r["wrong_open"] + assert date(2026, 6, 10) not in r["wrong_open"] + + def test_verifier_ignores_days_beyond_last_known_bar(self, monkeypatch): + """데이터가 아직 안 올라온 최근 날은 '휴장'이 아니라 '모름'이다. + + 장중에 돌리면 오늘 봉이 없어서 오늘이 휴장으로 오판됐다(실제 오탐). + """ + import tools.verify_trading_calendar as v + + monkeypatch.setattr( + v, "market_open_days", + lambda s, e, symbol=None: {date(2026, 6, 1), date(2026, 6, 2)}, + ) + r = v.compare(date(2026, 6, 1), date(2026, 6, 30)) + assert r["verified_through"] == date(2026, 6, 2) + assert r["wrong_open"] == [], "데이터 없는 미래 날짜를 휴장 오류로 보고했다" def test_verifier_does_not_report_when_data_unavailable(self, monkeypatch): """데이터 소스 장애를 달력 오류로 둔갑시키지 않는다.""" diff --git a/tools/verify_trading_calendar.py b/tools/verify_trading_calendar.py index 276616a7..b1e88f85 100644 --- a/tools/verify_trading_calendar.py +++ b/tools/verify_trading_calendar.py @@ -60,6 +60,13 @@ def compare(start: date, end: date) -> dict: if opened is None: return {"error": "시장 데이터를 가져오지 못해 대조 불가", "wrong_open": [], "wrong_closed": []} + # 데이터가 아직 안 올라온 최근 날은 '휴장'이 아니라 '모름'이다. 장중에 돌리면 + # 오늘 봉이 없어서 오늘이 휴장으로 오판된다 — 확인 범위를 지수의 마지막 봉까지로 + # 자른다. 없는 사실을 오류로 보고하지 않는 게 이 도구의 존재 이유다. + last_known = max(opened) + if end > last_known: + end = last_known + th = TradingHours(Config.get()) wrong_open: list[date] = [] wrong_closed: list[date] = [] @@ -77,7 +84,12 @@ def compare(start: date, end: date) -> dict: wrong_closed.append(d) d += timedelta(days=1) - return {"wrong_open": wrong_open, "wrong_closed": wrong_closed, "checked": checked} + return { + "wrong_open": wrong_open, + "wrong_closed": wrong_closed, + "checked": checked, + "verified_through": end, + } def main() -> int: @@ -96,7 +108,8 @@ def main() -> int: return 0 # 데이터 소스 장애를 달력 오류로 보고하지 않는다 wo, wc = result["wrong_open"], result["wrong_closed"] - print(f" 평일 {result['checked']}일 확인") + through = result.get("verified_through") + print(f" 평일 {result['checked']}일 확인 (~{through} 까지 — 지수 마지막 봉 기준)") if wo: print(f"\n ❌ 거래일로 판정했지만 실제 휴장 ({len(wo)}일) — 커버리지 분모 과대")