버그 및 크리티컬 에러 수정

This commit is contained in:
2026-02-23 00:30:23 +09:00
parent ae2f9cac6d
commit 1814f5e55e
4 changed files with 124 additions and 25 deletions

View File

@@ -16,6 +16,9 @@ import sys
from datetime import datetime, date
from pathlib import Path
import pytz
KST = pytz.timezone('Asia/Seoul')
sys.path.insert(0, str(Path(__file__).parent))
from sqlalchemy import select, delete, update, func
@@ -32,6 +35,7 @@ from app.services.watchlist import (
WATCHLIST_ENTRY_SCORE,
WATCHLIST_EXIT_SCORE,
)
from batch_daily_analysis import run_db_scanner
logging.basicConfig(
level=logging.INFO,
@@ -111,14 +115,14 @@ async def analyze_update_and_manage(
)
.values(
is_active=False,
removed_at=datetime.now(),
removed_at=datetime.now(KST),
removed_reason="price_filter",
)
)
await session.commit()
return False, None
now = datetime.now()
now = datetime.now(KST)
score = result.score
async with AsyncSessionLocal() as session:
@@ -336,7 +340,7 @@ async def refresh_watchlist(analysis_date: date, current_codes: set[str]) -> int
if not candidates:
return 0
now = datetime.now()
now = datetime.now(KST)
for row in candidates:
session.add(WatchlistStock(
stock_code=row.stock_code,
@@ -362,8 +366,8 @@ async def main():
logger.info("📴 장중이 아닙니다 (09:00~15:30 평일). 종료합니다.")
return
start_time = datetime.now()
analysis_date = date.today()
start_time = datetime.now(KST)
analysis_date = datetime.now(KST).date()
logger.info(
f"⚡ [Intraday] {start_time.strftime('%H:%M:%S')} 시작 — "
f"분석종목 재분석 (진입≥{WATCHLIST_ENTRY_SCORE}, 제거<{WATCHLIST_EXIT_SCORE})"
@@ -392,28 +396,77 @@ async def main():
]
results = await asyncio.gather(*tasks, return_exceptions=True)
success = sum(1 for r in results if isinstance(r, tuple) and r[0] is True)
# removed 카운터 수정: analyze_update_and_manage는 제거 시에도 True 반환하므로
# success(=분석 성공)에 removed가 포함됨 → retained = success - removed 로 표시
analyzed_ok = sum(1 for r in results if isinstance(r, tuple) and r[0] is True)
removed = sum(
1 for r in results
if isinstance(r, tuple) and r[0] is True
and r[1] is not None and r[1] < WATCHLIST_EXIT_SCORE
)
fail = sum(1 for r in results if isinstance(r, tuple) and r[0] is False)
retained = analyzed_ok - removed # 워치리스트에 실제 유지된 종목 수
fail = sum(1 for r in results if isinstance(r, tuple) and r[0] is False)
error = sum(1 for r in results if isinstance(r, Exception))
# 워치리스트 외부 후보 재분석 — score DB 최신화 (refresh_watchlist 전에 실행)
await rescore_outside_candidates(analysis_date, current_codes)
# 고점수 신규 종목 추가
# 장중 DB 스캐너 실행 — 장중 과매도 신규 종목 포착 (RSI≤30 + BB하단 + ADX≤30 + MA방향)
scanner_tickers = await run_db_scanner(analysis_date)
scanner_new = scanner_tickers - current_codes # 워치리스트 미포함 신규 신호 종목
added_scanner = 0
if scanner_new:
now_kst = datetime.now(KST)
async with AsyncSessionLocal() as session:
# 현재 활성 종목 수 확인
from sqlalchemy import func as sa_func
count_result = await session.execute(
select(sa_func.count())
.select_from(WatchlistStock)
.where(WatchlistStock.is_active == True)
)
active_count = count_result.scalar() or 0
slots = WATCHLIST_MAX_SIZE - active_count
for code in list(scanner_new)[:slots]:
# stock_analysis에서 종목명 조회
name_result = await session.execute(
select(StockAnalysis.stock_name)
.where(
StockAnalysis.stock_code == code,
StockAnalysis.analysis_date == analysis_date,
)
.limit(1)
)
name_row = name_result.first()
if not name_row:
continue
session.add(WatchlistStock(
stock_code=code,
stock_name=name_row.stock_name,
added_at=now_kst,
added_reason="intraday_scanner",
score_at_add=None,
is_active=True,
))
added_scanner += 1
await session.commit()
if added_scanner:
logger.info(f"📡 장중 스캐너 신규 추가: {added_scanner}개 (RSI≤30+BB하단)")
current_codes |= scanner_new # refresh_watchlist 중복 방지용 갱신
# 고점수 신규 종목 추가 (스캐너 추가분 제외)
added = await refresh_watchlist(analysis_date, current_codes)
duration = (datetime.now() - start_time).total_seconds()
duration = (datetime.now(KST) - start_time).total_seconds()
logger.info(
f"✅ [Intraday] 완료: {success}/{total} 성공, "
f"{removed} 제거, +{added} 신규, "
f"✅ [Intraday] 완료: {retained}/{total} 유지, "
f"{removed} 제거, +{added} 신규(score), +{added_scanner} 신규(스캐너), "
f"{fail} 실패, {error} 오류 — {duration:.1f}초"
)
if __name__ == "__main__":
asyncio.run(main())