전략 취약점 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:
2026-04-03 11:41:52 +09:00
parent eda5d76af9
commit 5f0f839039
22 changed files with 299 additions and 132 deletions

View File

@@ -99,6 +99,7 @@ class OpeningRange:
low: float
total_volume: int
avg_vol_per_min: float # 분당 평균 거래량 (ORB 기간)
open_price: float = 0.0 # M4: 시가 (첫 ORB 봉의 open, gap 필터용)
@dataclass
@@ -253,11 +254,14 @@ def calculate_opening_range(candles_1min: List[MinuteCandle]) -> Optional[Openin
if len(orb) < 5:
return None
total_vol = sum(c.volume for c in orb)
# M4: 시가 = ORB 기간 첫 봉의 open (gap 필터용)
sorted_orb = sorted(orb, key=lambda c: c.time)
return OpeningRange(
high=max(c.high for c in orb),
low=min(c.low for c in orb),
total_volume=total_vol,
avg_vol_per_min=total_vol / len(orb),
open_price=sorted_orb[0].open,
)
@@ -302,6 +306,22 @@ def check_orb_signal(
if not orb or current_price <= 0:
return None
# M4: Gap filter — 시가가 전일종가 대비 4%+ 갭이면 ORB 돌파 위험 과대, 진입 거부
# prev_close 근사: candles_1min 첫 봉의 open 이전 데이터 없으므로 orb.open_price 사용
# orb.open_price = 09:00 시가. 현재가와 시가의 괴리가 4% 초과면 갭 상승 종목으로 간주
if orb.open_price > 0:
_gap_ratio = abs(current_price - orb.open_price) / orb.open_price
if _gap_ratio > 0.04:
logger.debug("ORB skip %s: gap ratio %.2f%% > 4%%", stock_code, _gap_ratio * 100)
return None
# M4: Range width filter — ORB 범위가 너무 좁으면 노이즈 돌파 위험
if orb.low > 0:
_range_width = (orb.high - orb.low) / orb.low
if _range_width < 0.003: # 최소 0.3% 범위
logger.debug("ORB skip %s: range width %.3f%% < 0.3%%", stock_code, _range_width * 100)
return None
if current_price < orb.high * (1 + settings.DAY_TRADE_ORB_BREAKOUT_MARGIN):
return None