fix: 개장 시 추천 종목 지연 — 배치 분석 개장 전 선행 실행 + 스케줄러 개장 전 기상

모든 스케줄러가 장 마감 중 고정 1800초를 자서 09:00 개장을 최대 30분 건너뛰었고,
배치 분석은 개장 후에야 시작돼 추천 종목이 09:00에 비어 있었음.
- market_schedule.is_pre_market_window() 추가 (08:30~09:00, 거래일만)
- batch_analysis_scheduler: 개장 전 준비 구간에도 분석 실행 → stock_analysis·Redis 추천
  캐시를 미리 채워 09:00에 추천 즉시 표시
- batch/auto_trade/swing 스케줄러: 마감 중 sleep = min(1800, 개장까지 남은 시간)
- 테스트 1건 추가 (총 5건)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 09:07:57 +09:00
parent 546cf17edb
commit 9fd02932fa
4 changed files with 44 additions and 7 deletions

View File

@@ -13,7 +13,9 @@ from datetime import datetime as _dt, date as _date, timedelta as _td
from app.core.config import settings
from app.core.database import AsyncSessionLocal
from app.core.market_schedule import is_market_open
from app.core.market_schedule import (
is_market_open, seconds_until_market_open, is_pre_market_window,
)
logger = logging.getLogger(__name__)
@@ -102,8 +104,12 @@ async def batch_analysis_scheduler():
await asyncio.sleep(sleep_sec)
continue
if not is_market_open():
await asyncio.sleep(1800)
# [FIX 2026-09-23] 개장 전 준비 구간(08:30~09:00)에는 분석을 미리 돌려
# stock_analysis·Redis 추천 캐시를 채워둔다 → 09:00에 추천 종목 즉시 표시.
# 그 외 마감 시간에는 개장을 넘겨 자지 않도록 sleep 상한을 둔다.
if not is_market_open() and not is_pre_market_window():
_until_open = seconds_until_market_open()
await asyncio.sleep(1800.0 if _until_open is None else max(15.0, min(1800.0, _until_open)))
continue
# ── exponential backoff 재시도 (최대 3회) ────────────────────────
@@ -411,8 +417,11 @@ async def auto_trade_scheduler():
while True:
try:
if not is_market_open():
logger.info("💤 Market is closed. Next check in 30 minutes.")
await asyncio.sleep(1800)
# 개장을 넘겨 자지 않도록 상한 (09:00부터 바로 매매 판단 시작)
_until_open = seconds_until_market_open()
_sleep = 1800.0 if _until_open is None else max(15.0, min(1800.0, _until_open))
logger.info(f"💤 Market is closed. Next check in {_sleep / 60:.0f} minutes.")
await asyncio.sleep(_sleep)
continue
async with AsyncSessionLocal() as session:

View File

@@ -99,8 +99,11 @@ async def swing_scheduler() -> None:
logger.info("[Swing] ── 사이클 시작 ──")
if not is_market_open():
logger.info("[Swing] 장 미오픈 — 30분 대기")
await asyncio.sleep(1800)
logger.info("[Swing] 장 미오픈 — 개장까지 대기")
# 개장을 넘겨 자지 않도록 상한 (2026-09-23)
from app.core.market_schedule import seconds_until_market_open
_until_open = seconds_until_market_open()
await asyncio.sleep(1800.0 if _until_open is None else max(15.0, min(1800.0, _until_open)))
continue
# ── Phase 1: 청산 체크 (별도 세션) ──