버그 및 크리티컬 에러 수정
This commit is contained in:
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user