""" 시장 레짐 감지 모듈 - 코스피 지수 수준에 따른 시장 레짐 분류 - 코스피 6300 목표 수준에서의 모델 적합성 평가 - 레짐별 전략 파라미터 자동 조정 """ from dataclasses import dataclass from enum import Enum from typing import Optional, Dict class MarketRegime(Enum): BULL_EXTREME = "bull_extreme" # 코스피 5000+ (역사적 극고점, 6300 시나리오) BULL_STRONG = "bull_strong" # 코스피 3500~5000 (강한 상승장) BULL_NORMAL = "bull_normal" # 코스피 2500~3500 (정상 상승장) SIDEWAYS = "sideways" # 코스피 2000~2500 (횡보) BEAR_MILD = "bear_mild" # 코스피 1500~2000 (약세) BEAR_SEVERE = "bear_severe" # 코스피 1500 미만 (심각한 약세) @dataclass class RegimeAnalysis: """레짐 분석 결과""" regime: MarketRegime kospi_level: float description: str recommended_strategy: str buy_threshold_adj: float # 매수 임계값 조정치 (+: 더 엄격, -: 완화) position_size_adj: float # 포지션 크기 조정 배수 (1.0 = 기본) lstm_weight_adj: float # LSTM 앙상블 가중치 조정 (+0.1 = 10% 증가) model_recommendation: str # 모델 유지/교체 권고 risk_level: str # LOW / MEDIUM / HIGH / EXTREME class MarketRegimeDetector: """ 코스피 지수 수준 기반 시장 레짐 감지기 코스피 6300 시나리오: - 현재 한국 증시 역대 최고점(2021년 3300) 대비 약 2배 수준 - BULL_EXTREME 레짐에 해당 → LSTM 단독 의존 지양, Transformer/Mamba 검토 필요 - 추세 추종 강화 + 고점 리스크 관리 병행 """ # 레짐별 상세 파라미터 _REGIME_PARAMS: Dict[MarketRegime, dict] = { MarketRegime.BULL_EXTREME: { "description": "코스피 극강세장 5000+ (6300 시나리오)", "recommended_strategy": ( "추세 추종 극대화, 트레일링 스탑 확대(ATR×4), " "고점 과열 구간으로 포지션 축소 병행" ), "buy_threshold_adj": -0.04, # 강세 모멘텀 → 진입 소폭 완화 "position_size_adj": 0.75, # 고점 리스크로 포지션 축소 "lstm_weight_adj": -0.12, # LSTM 비중 축소 (비선형 가격 동작) "model_recommendation": ( "Temporal Fusion Transformer(TFT) 또는 Mamba(SSM) 교체 권고 - " "LSTM은 극강세 과열 구간에서 비선형 가격 동작 포착 한계" ), "risk_level": "EXTREME", }, MarketRegime.BULL_STRONG: { "description": "코스피 강상승장 3500~5000", "recommended_strategy": "추세 추종, 모멘텀 강화, 손절 완화(ATR×2.5)", "buy_threshold_adj": -0.03, "position_size_adj": 1.1, "lstm_weight_adj": 0.05, "model_recommendation": "현재 LSTM v3 적합 - 성능 모니터링 유지", "risk_level": "MEDIUM", }, MarketRegime.BULL_NORMAL: { "description": "코스피 정상 상승장 2500~3500", "recommended_strategy": "기본 전략 유지 (기술+LSTM+LLM 균형)", "buy_threshold_adj": 0.0, "position_size_adj": 1.0, "lstm_weight_adj": 0.0, "model_recommendation": "현재 LSTM v3 최적 환경", "risk_level": "LOW", }, MarketRegime.SIDEWAYS: { "description": "코스피 횡보장 2000~2500", "recommended_strategy": "박스권 매매, LLM 감성 비중 확대, 빠른 익절", "buy_threshold_adj": 0.03, "position_size_adj": 0.85, "lstm_weight_adj": -0.05, "model_recommendation": "현재 LSTM v3 적합 - 감성 분석 가중치 강화", "risk_level": "LOW", }, MarketRegime.BEAR_MILD: { "description": "코스피 약세장 1500~2000", "recommended_strategy": "현금 비중 확대(50%+), 방어주 선별 매수", "buy_threshold_adj": 0.08, "position_size_adj": 0.5, "lstm_weight_adj": 0.0, "model_recommendation": "현재 LSTM v3 적합 - 리스크 관리 파라미터 강화", "risk_level": "HIGH", }, MarketRegime.BEAR_SEVERE: { "description": "코스피 극약세장 1500 미만", "recommended_strategy": "전면 현금화, 매수 중단", "buy_threshold_adj": 0.20, "position_size_adj": 0.2, "lstm_weight_adj": 0.0, "model_recommendation": "매크로 팩터 기반 방어 모델 전환 필요", "risk_level": "EXTREME", }, } @classmethod def detect( cls, kospi_price: float, kospi_change_pct: float = 0.0, volatility_20d: float = 0.0, ) -> RegimeAnalysis: """ 코스피 지수 수준 + 변동성으로 시장 레짐 감지 Args: kospi_price: 현재 코스피 지수 (예: 2600, 6300) kospi_change_pct: 전일 대비 등락률 (%) volatility_20d: 20일 변동성 (선택, 0이면 무시) Returns: RegimeAnalysis: 레짐 분석 결과 및 전략 파라미터 """ # 1. 지수 수준으로 기본 레짐 결정 if kospi_price >= 5000: regime = MarketRegime.BULL_EXTREME elif kospi_price >= 3500: regime = MarketRegime.BULL_STRONG elif kospi_price >= 2500: regime = MarketRegime.BULL_NORMAL elif kospi_price >= 2000: regime = MarketRegime.SIDEWAYS elif kospi_price >= 1500: regime = MarketRegime.BEAR_MILD else: regime = MarketRegime.BEAR_SEVERE params = cls._REGIME_PARAMS[regime] # 2. 변동성 기반 포지션 사이징 추가 조정 position_adj = params["position_size_adj"] if volatility_20d > 30: position_adj *= 0.6 # 극단적 변동성 → 추가 50% 축소 elif volatility_20d > 20: position_adj *= 0.8 # 높은 변동성 → 20% 축소 # 3. 급락 중 레짐 하향 조정 (패닉 감지) if kospi_change_pct <= -3.0: # 극단적 일일 급락 → 포지션 추가 축소 position_adj *= 0.5 print(f"[Regime] PANIC DETECTED (일일 {kospi_change_pct:.1f}%) → 포지션 50% 추가 축소") return RegimeAnalysis( regime=regime, kospi_level=kospi_price, description=params["description"], recommended_strategy=params["recommended_strategy"], buy_threshold_adj=params["buy_threshold_adj"], position_size_adj=round(position_adj, 3), lstm_weight_adj=params["lstm_weight_adj"], model_recommendation=params["model_recommendation"], risk_level=params["risk_level"], ) @classmethod def validate_model_for_regime( cls, regime: MarketRegime, backtest_sharpe: Optional[float] = None, backtest_winrate: Optional[float] = None, backtest_mdd: Optional[float] = None, ) -> dict: """ 현재 LSTM v3 모델이 해당 레짐에서 적합한지 검증 Returns: { "is_suitable": bool, "confidence_score": float (0~1), "recommendation": str, "should_replace": bool, "alternative_models": list[str], "reason": str, } """ result = { "is_suitable": True, "confidence_score": 0.75, "recommendation": "현재 LSTM v3 모델 유지", "should_replace": False, "alternative_models": [], "reason": "정상 상승장 구간 - LSTM v3 최적 환경", } # 레짐 기반 기본 평가 if regime == MarketRegime.BULL_EXTREME: result.update({ "is_suitable": False, "confidence_score": 0.38, "recommendation": "Transformer 계열 모델 교체 강력 권고", "should_replace": True, "alternative_models": [ "Temporal Fusion Transformer (TFT) - 장기 시계열 최강", "Mamba (SSM) - 초고속 추론 + 긴 컨텍스트", "PatchTST - Transformer 기반 주가 예측 특화", "TimesNet - 2D 시계열 변환 + CNN", "N-BEATS / N-HiTS - 해석 가능 딥러닝", ], "reason": ( "코스피 5000+ 극강세장에서 LSTM은 비선형적 가격 급등 패턴을 " "충분히 학습하지 못함. Attention 메커니즘만으로는 장기 상승 추세의 " "복잡한 의존성 포착에 한계 존재." ), }) elif regime == MarketRegime.BEAR_SEVERE: result.update({ "is_suitable": False, "confidence_score": 0.30, "recommendation": "매크로 팩터 + Regime-Switching 모델 교체 권고", "should_replace": True, "alternative_models": [ "Regime-Switching LSTM (HMM + LSTM)", "매크로 멀티팩터 모델 (환율, 금리, VIX 통합)", "GRU + Attention (LSTM 경량 대안)", ], "reason": "극약세장에서는 기술적 지표보다 거시경제 팩터가 지배적", }) elif regime == MarketRegime.BULL_STRONG: result.update({ "confidence_score": 0.72, "reason": "강상승장 - LSTM 추세 학습 양호하나 성능 모니터링 필요", }) elif regime == MarketRegime.SIDEWAYS: result.update({ "confidence_score": 0.68, "reason": "횡보장 - LSTM 예측력 저하, LLM 감성 보완 필수", "recommendation": "현재 LSTM v3 유지 + LLM 감성 가중치 상향", }) # 백테스트 결과 반영 if backtest_sharpe is not None: if backtest_sharpe < 0: result["confidence_score"] *= 0.5 result["should_replace"] = True result["recommendation"] += " ⚠️ Sharpe < 0 → 즉시 교체 검토" elif backtest_sharpe < 0.5: result["confidence_score"] *= 0.75 result["recommendation"] += f" (Sharpe={backtest_sharpe:.2f} 미흡)" if backtest_winrate is not None and backtest_winrate < 45: result["confidence_score"] *= 0.8 result["recommendation"] += f" (승률={backtest_winrate:.1f}% 미흡)" if backtest_mdd is not None and backtest_mdd < -25: result["confidence_score"] *= 0.7 result["should_replace"] = True result["recommendation"] += f" ⚠️ MDD={backtest_mdd:.1f}% 과다" result["confidence_score"] = round(max(0.0, min(1.0, result["confidence_score"])), 3) return result @staticmethod def get_regime_label(kospi_price: float) -> str: """간략 레짐 라벨 반환 (로그/UI 표시용)""" if kospi_price >= 5000: return f"BULL_EXTREME({kospi_price:.0f})" elif kospi_price >= 3500: return f"BULL_STRONG({kospi_price:.0f})" elif kospi_price >= 2500: return f"BULL_NORMAL({kospi_price:.0f})" elif kospi_price >= 2000: return f"SIDEWAYS({kospi_price:.0f})" elif kospi_price >= 1500: return f"BEAR_MILD({kospi_price:.0f})" return f"BEAR_SEVERE({kospi_price:.0f})"