feat(v3.2): DailyLedger + RiskGate + news_snapshot + backtest_runner

- DailyLedger: 당일 매수 회계 + 연속 손절 카운터 + 매수 신호 점수 한 객체로 집약 (bot.py 정리)
- RiskGate: 테마당 동시 보유 + 노출 비율 상한 검증 (포트폴리오 레벨)
- news_snapshot: 뉴스 SQLite 영구 저장 + 사후 감성 재검증 인프라
- backtest_runner: 전 종목 KIS 일봉 기반 백테스트 (Sharpe/MDD/Calmar)
- bot.py 274 line 정리 (DailyLedger 분리)
- backtest.py 173 line 재작성 (v3.2 next-bar 체결 + 거래세)
- daily_launcher.py 폐기 (warmup_and_restart 통합)
- .gitignore: .claude/ 제외

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 02:57:26 +09:00
parent 0aebca7ff0
commit 42b91d03cf
12 changed files with 869 additions and 492 deletions

View File

@@ -6,17 +6,12 @@
"""
import os
import time
import datetime
import threading
from pathlib import Path
from multiprocessing.shared_memory import SharedMemory
from modules.config import Config
# EOD 마커 파일: 오늘 장 마감 후 봇이 기록, Watchdog가 재시작 여부 결정에 사용
_EOD_DATE_FILE = Path("data") / ".eod_date"
class ProcessTracker:
"""메모리 기반 프로세스 추적기"""
@@ -141,17 +136,6 @@ class ProcessWatchdog:
entry = self._watched.get(name)
return entry['process'] if entry else None
@staticmethod
def is_eod_today() -> bool:
"""오늘 EOD 마커 파일이 존재하면 True (장 마감 셧다운 → 재시작 차단)"""
try:
if not _EOD_DATE_FILE.exists():
return False
eod_date = datetime.date.fromisoformat(_EOD_DATE_FILE.read_text().strip())
return eod_date >= datetime.date.today()
except Exception:
return False
def _watchdog_loop(self):
"""주기적으로 자식 프로세스 상태 확인"""
import multiprocessing
@@ -170,11 +154,6 @@ class ProcessWatchdog:
exit_code = proc.exitcode
restart_count = entry['restart_count']
# [EOD 차단] 오늘 장 마감 셧다운이면 재시작하지 않음
if ProcessWatchdog.is_eod_today():
print(f"[Watchdog] {name}: EOD 셧다운 감지 — 재시작 건너뜀.")
continue
if restart_count >= Config.MAX_RESTART_COUNT:
print(f"[Watchdog] {name} crashed (exit={exit_code}). "
f"Max restarts ({Config.MAX_RESTART_COUNT}) reached. Giving up.")