전략 취약점 15건 수정 (코드 리뷰 기반)
## CRITICAL/HIGH (S1-S8) - S1: Aggressive 모드 %B 스케일링 캡 1.3 (BB 중앙선 위 매수 방지) - S2: ML scaler train-only fit (데이터 누수 제거) - S3: SIDEWAYS = ADX<20 AND slope≈0 (트렌드 초기 차단 해소) - S4: EOD 청산 실패 시 재시도 + CRITICAL 로그 - S5: 일일 손실 한도에 미실현 P&L 포함 - S6: trailing stop 고점 매 사이클 갱신 - S7: 섹터 필터 미매핑 종목 "기타" 분류 (max 3 적용) - S8: 스캐너 RSI 기준 25→46, BB 점수 -3%~+1.5% 구간 ## MEDIUM (M1-M7) - M1: 스캐너 최소 거래량 100만→10만주 - M2: ADX 히스테리시스 18/22 (국면 플리핑 방지) - M3: 분할매도 최소 수익 1.0%→0.5% - M4: ORB 갭 필터 4% + 레인지 폭 0.3% 최소 - M5: 쿨다운 스트릭 부분매도 시 미변경 - M6: MDD 강제매도 loss_tracker 기록 제거 (허위 ban 방지) - M7: RSI Wilder 스무딩으로 통일 ## 인프라/보안 - 인증 누락 엔드포인트 수정 (analysis, journals) - nginx 보안 헤더 location 블록 누락 수정 - Alembic 마이그레이션 IF NOT EXISTS 가드 추가 - SellRequest/UsOrderRequest 입력 검증 강화 - 차트 네이버 폴백 추가 (DB 데이터 없을 때) - 서버 이전 시 자동 DB 마이그레이션 + KRX 초기 수집 - CLAUDE.md 섹션 5.1 백테스트 값 동기화 (4%→6%, -6%→-7%) - MAX_SWING_POSITIONS 25→20 (CLAUDE.md 준수) - mdd_manager failed_count에서 skip 종목 제외 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -147,6 +147,48 @@ async def lifespan(app: FastAPI):
|
||||
logger.error(traceback.format_exc())
|
||||
raise
|
||||
|
||||
# ── Alembic 자동 마이그레이션 (서버 이전 시 DB 스키마 자동 동기화) ──────
|
||||
try:
|
||||
from alembic.config import Config as AlembicConfig
|
||||
from alembic import command as alembic_command
|
||||
import os
|
||||
alembic_ini = os.path.join(os.path.dirname(os.path.dirname(__file__)), "alembic.ini")
|
||||
if os.path.exists(alembic_ini):
|
||||
alembic_cfg = AlembicConfig(alembic_ini)
|
||||
alembic_cfg.set_main_option("script_location", os.path.join(os.path.dirname(os.path.dirname(__file__)), "alembic"))
|
||||
alembic_command.upgrade(alembic_cfg, "head")
|
||||
logger.info("✅ Alembic migration completed (upgrade head)")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ Alembic migration 실패 (non-critical): {e}")
|
||||
|
||||
# ── KRX 일봉 데이터 초기 수집 (DB 비어있을 때만 백그라운드 실행) ──────
|
||||
try:
|
||||
from app.models.models import StockDailyPrice
|
||||
from sqlalchemy import select as _select, func as _func
|
||||
async with AsyncSessionLocal() as _check_session:
|
||||
_count = await _check_session.execute(
|
||||
_select(_func.count()).select_from(StockDailyPrice)
|
||||
)
|
||||
_total = _count.scalar() or 0
|
||||
if _total == 0 and settings.DATAGOKR_API_KEY:
|
||||
async def _initial_krx_collect():
|
||||
await asyncio.sleep(10) # 부팅 안정화 대기
|
||||
try:
|
||||
from collect_krx_data import collect_all
|
||||
logger.info("📥 [KRX] 초기 데이터 수집 시작 (stock_daily_prices 비어있음)...")
|
||||
await collect_all(update_mode=True)
|
||||
logger.info("✅ [KRX] 초기 데이터 수집 완료")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ [KRX] 초기 데이터 수집 실패 (non-critical): {e}")
|
||||
asyncio.create_task(_initial_krx_collect())
|
||||
logger.info("📥 [KRX] stock_daily_prices 비어있음 — 백그라운드 초기 수집 예약됨")
|
||||
elif _total == 0:
|
||||
logger.warning("⚠️ [KRX] stock_daily_prices 비어있으나 DATAGOKR_API_KEY 미설정 — 네이버 차트 폴백 사용")
|
||||
else:
|
||||
logger.info(f"📊 [KRX] stock_daily_prices: {_total:,}행 확인")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ [KRX] 초기 데이터 확인 실패 (non-critical): {e}")
|
||||
|
||||
try:
|
||||
from app.api.endpoints.day_trading import restore_positions_from_db
|
||||
await restore_positions_from_db()
|
||||
|
||||
Reference in New Issue
Block a user