feat(stock-lab): /run mode=auto 공휴일·주말 skipped_holiday 처리
This commit is contained in:
@@ -17,6 +17,29 @@ from .registry import NODE_REGISTRY, GATE_REGISTRY
|
|||||||
router = APIRouter(prefix="/api/stock/screener")
|
router = APIRouter(prefix="/api/stock/screener")
|
||||||
|
|
||||||
|
|
||||||
|
import json as _json
|
||||||
|
import pathlib as _pathlib
|
||||||
|
|
||||||
|
_HOLIDAYS_CACHE = None
|
||||||
|
|
||||||
|
|
||||||
|
def _holidays():
|
||||||
|
global _HOLIDAYS_CACHE
|
||||||
|
if _HOLIDAYS_CACHE is None:
|
||||||
|
path = _pathlib.Path(__file__).resolve().parent.parent / "holidays.json"
|
||||||
|
try:
|
||||||
|
with path.open(encoding="utf-8") as f:
|
||||||
|
data = _json.load(f)
|
||||||
|
_HOLIDAYS_CACHE = set(data) if isinstance(data, list) else set(data.keys())
|
||||||
|
except FileNotFoundError:
|
||||||
|
_HOLIDAYS_CACHE = set()
|
||||||
|
return _HOLIDAYS_CACHE
|
||||||
|
|
||||||
|
|
||||||
|
def _is_holiday(d: dt.date) -> bool:
|
||||||
|
return d.weekday() >= 5 or d.isoformat() in _holidays()
|
||||||
|
|
||||||
|
|
||||||
def _db_path() -> str:
|
def _db_path() -> str:
|
||||||
return os.environ.get("STOCK_DB_PATH", "/app/data/stock.db")
|
return os.environ.get("STOCK_DB_PATH", "/app/data/stock.db")
|
||||||
|
|
||||||
@@ -147,6 +170,17 @@ def post_run(body: schemas.RunRequest):
|
|||||||
started_at = dt.datetime.utcnow().isoformat()
|
started_at = dt.datetime.utcnow().isoformat()
|
||||||
with _conn() as c:
|
with _conn() as c:
|
||||||
asof = _resolve_asof(body.asof, c)
|
asof = _resolve_asof(body.asof, c)
|
||||||
|
|
||||||
|
# Skipped holiday handling for mode='auto'
|
||||||
|
if body.mode == "auto" and _is_holiday(asof):
|
||||||
|
return schemas.RunResponse(
|
||||||
|
asof=asof.isoformat(), mode="auto", status="skipped_holiday",
|
||||||
|
run_id=None, survivors_count=None,
|
||||||
|
weights={}, top_n=0,
|
||||||
|
results=[], telegram_payload=None,
|
||||||
|
warnings=[f"{asof.isoformat()} is a holiday — skipped"],
|
||||||
|
)
|
||||||
|
|
||||||
defaults = _load_settings(c)
|
defaults = _load_settings(c)
|
||||||
|
|
||||||
if body.mode == "auto":
|
if body.mode == "auto":
|
||||||
|
|||||||
@@ -144,3 +144,11 @@ def test_runs_list_and_detail(client):
|
|||||||
assert detail.status_code == 200
|
assert detail.status_code == 200
|
||||||
assert detail.json()["meta"]["id"] == run_id
|
assert detail.json()["meta"]["id"] == run_id
|
||||||
assert isinstance(detail.json()["results"], list)
|
assert isinstance(detail.json()["results"], list)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_holiday_returns_skipped(client):
|
||||||
|
# 2026-05-09는 토요일 (주말). _is_holiday 가 weekday>=5를 잡음.
|
||||||
|
r = client.post("/api/stock/screener/run",
|
||||||
|
json={"mode": "auto", "asof": "2026-05-09"})
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert r.json()["status"] == "skipped_holiday"
|
||||||
|
|||||||
Reference in New Issue
Block a user