""" 애플리케이션 설정 모듈 - pydantic-settings 기반 .env 파일 로드 - DB, Redis, KIS API, AI 프로바이더 등 전역 설정 관리 """ import os from pathlib import Path from pydantic_settings import BaseSettings from functools import lru_cache # 프로젝트 루트 디렉토리 (backend의 상위 디렉토리) BASE_DIR = Path(__file__).resolve().parent.parent.parent BACKEND_ENV_FILE = BASE_DIR / ".env" class Settings(BaseSettings): # Application APP_NAME: str = "Auto Stock Trading System" ENVIRONMENT: str = "development" DEBUG: bool = False # 운영 기본값 False (.env에서 DEBUG=true로 개발 모드 활성화) SECRET_KEY: str = "change-this-secret-key" # Server BACKEND_HOST: str = "0.0.0.0" BACKEND_PORT: int = 8000 # Database DATABASE_URL: str = "postgresql://autostock:autostock_password@localhost:5432/autostock_db" # Redis REDIS_URL: str = "redis://localhost:6379/0" # RabbitMQ RABBITMQ_URL: str = "amqp://autostock:autostock_password@localhost:5672/" # Korea Investment API - 모드 선택 KIS_MODE: str = "virtual" # "virtual" (모의투자) 또는 "real" (실전투자) # 모의투자 설정 KIS_VIRTUAL_APP_KEY: str = "" KIS_VIRTUAL_APP_SECRET: str = "" KIS_VIRTUAL_ACCOUNT_NO: str = "" KIS_VIRTUAL_URL: str = "https://openapivts.koreainvestment.com:29443" # 실전투자 설정 KIS_REAL_APP_KEY: str = "" KIS_REAL_APP_SECRET: str = "" KIS_REAL_ACCOUNT_NO: str = "" KIS_REAL_URL: str = "https://openapi.koreainvestment.com:9443" # 실제 사용되는 KIS API 설정 (KIS_MODE에 따라 자동 결정) KOREA_INVESTMENT_APP_KEY: str = "" KOREA_INVESTMENT_APP_SECRET: str = "" KOREA_INVESTMENT_ACCOUNT_NUMBER: str = "" KOREA_INVESTMENT_API_BASE_URL: str = "https://openapi.koreainvestment.com:9443" # 레거시 호환 (직접 지정 시 우선) KIS_APP_KEY: str = "" KIS_APP_SECRET: str = "" KIS_ACCOUNT_NO: str = "" KIS_BASE_URL: str = "" def __init__(self, **kwargs): super().__init__(**kwargs) self._apply_kis_mode() def _apply_kis_mode(self): """KIS_MODE에 따라 모의/실전 API 설정을 자동 선택""" is_real = self.KIS_MODE.lower() == "real" # 1순위: KIS_MODE에 따른 VIRTUAL/REAL 변수 if is_real: mode_key = self.KIS_REAL_APP_KEY mode_secret = self.KIS_REAL_APP_SECRET mode_account = self.KIS_REAL_ACCOUNT_NO mode_url = self.KIS_REAL_URL else: mode_key = self.KIS_VIRTUAL_APP_KEY mode_secret = self.KIS_VIRTUAL_APP_SECRET mode_account = self.KIS_VIRTUAL_ACCOUNT_NO mode_url = self.KIS_VIRTUAL_URL # 2순위: 레거시 KIS_APP_KEY 등 (직접 지정된 경우) fallback_key = self.KIS_APP_KEY fallback_secret = self.KIS_APP_SECRET fallback_account = self.KIS_ACCOUNT_NO fallback_url = self.KIS_BASE_URL # 최종 적용 (mode > legacy > KOREA_INVESTMENT_ 기존값) self.KOREA_INVESTMENT_APP_KEY = mode_key or fallback_key or self.KOREA_INVESTMENT_APP_KEY self.KOREA_INVESTMENT_APP_SECRET = mode_secret or fallback_secret or self.KOREA_INVESTMENT_APP_SECRET self.KOREA_INVESTMENT_ACCOUNT_NUMBER = mode_account or fallback_account or self.KOREA_INVESTMENT_ACCOUNT_NUMBER self.KOREA_INVESTMENT_API_BASE_URL = mode_url or fallback_url or self.KOREA_INVESTMENT_API_BASE_URL @property def is_virtual_trading(self) -> bool: """모의투자 모드 여부""" return self.KIS_MODE.lower() != "real" # Claude AI ANTHROPIC_API_KEY: str = "" # MCP Server MCP_SERVER_URL: str = "http://localhost:8001" # MCP AI 검증 (자동매매 전 AI 최종 판단) MCP_VALIDATION_ENABLED: bool = True # 기본 ON — AI 검증 없이 자동매매 차단 MCP_CONFIDENCE_THRESHOLD: float = 0.5 # AI 신뢰도 최소값 (0.0~1.0) # News API NEWS_API_KEY: str = "" # Logging LOG_LEVEL: str = "INFO" # Trading Settings (CLAUDE.md 섹션 6 기반) MAX_POSITION_SIZE: float = 0.04 # 1종목당 최대 3-4% (섹션 6.1) MAX_DAILY_TRADES: int = 5 STOP_LOSS_PERCENT: float = -6.0 # 저변동 종목 손절 -6% (섹션 6.2) TAKE_PROFIT_PERCENT: float = 5.0 # 익절 기본값 AUTO_TRADE_ENABLED: bool = True # 포지션 규칙 (섹션 6.1) MAX_HOLDINGS_COUNT: int = 20 # 동시 보유 최대 20종목 LEVERAGE_ETF_MAX_RATIO: float = 0.20 # 레버리지/인버스 ETF 20% 이내 # ATR 기반 손절/트레일링 (섹션 6.2) STOP_LOSS_HIGH_VOL: float = -10.0 # 고변동 종목 손절 -10% ATR_STOP_MULTIPLIER: float = 2.5 # 2.5 × ATR 손절 ATR_TRAILING_MULTIPLIER: float = 1.5 # 1.5 × ATR 트레일링 # MDD 관리 (섹션 6.3) MDD_PAUSE_THRESHOLD: float = -10.0 # -10% MDD: 신규진입 중단 + 30% 축소 MDD_FORCE_THRESHOLD: float = -20.0 # -20% MDD: 50% 강제 축소 # 가격 필터 (섹션 6.4) MIN_PRICE_KRW: int = 5000 # 국내 5,000원 이하 매수 금지 MIN_PRICE_USD: float = 4.0 # 미국 $4 이하 매수 금지 # 연속 손실 관리 (섹션 7.4) CONSECUTIVE_LOSS_BAN_COUNT: int = 3 # 3회 연속 손실 CONSECUTIVE_LOSS_BAN_DAYS: int = 30 # 30거래일 진입 금지 # 평균회귀 전략 자본 배분 (섹션 7.5) MEAN_REVERSION_MAX_CAPITAL: float = 0.30 # 전체 자본의 30% 이내 # Trend filter (MA-based) TREND_FILTER_ENABLED: bool = False TREND_MA_SHORT: int = 20 TREND_MA_LONG: int = 60 TREND_TOLERANCE_PCT: float = 0.005 # 급등 과열 필터 (고점 추격 매수 방지) OVERHEAT_FILTER_ENABLED: bool = True OVERHEAT_3DAY_MAX: float = 15.0 OVERHEAT_5DAY_MAX: float = 20.0 OVERHEAT_10DAY_MAX: float = 30.0 OVERHEAT_DISPARITY_MAX: float = 108.0 # 하락장 방어 필터 BEAR_MARKET_FILTER_ENABLED: bool = True BEAR_MARKET_MIN_SCORE: int = 5 BEAR_MARKET_MAX_TRADES: int = 3 BEAR_MARKET_POSITION_RATIO: float = 0.5 # 데이트레이딩 설정 (CLAUDE.md 섹션 8.2) DAY_TRADE_ENABLED: bool = True # 데이트레이딩 활성화 DAY_TRADE_MAX_POSITIONS: int = 3 # 동시 최대 종목 수 DAY_TRADE_CAPITAL_RATIO: float = 0.25 # 전체 자본 대비 최대 25% DAY_TRADE_PER_STOCK_RATIO: float = 0.10 # 종목당 최대 10% DAY_TRADE_STOP_LOSS_PCT: float = 0.02 # 종목 손절 -2% DAY_TRADE_DAILY_LOSS_LIMIT: float = -0.03 # 일일 손실 한도 -3% DAY_TRADE_EOD_FLAT_HOUR: int = 15 # EOD 강제 청산 시 (15시) DAY_TRADE_EOD_FLAT_MIN: int = 20 # EOD 강제 청산 분 (20분) DAY_TRADE_ORB_VOL_MULT: float = 1.5 # ORB 돌파 거래량 배수 DAY_TRADE_TRAILING_TRIGGER: float = 0.015 # 트레일링 발동 수익률 # ML / TensorFlow integration toggle ML_TF_ENABLED: bool = True # ML 모델 기반 보조지표 사용 # ML Ensemble (LightGBM + LSTM + RF) ML_ENSEMBLE_ENABLED: bool = True ML_ENSEMBLE_MODEL_DIR: str = "models/ensemble" ML_ENSEMBLE_RULE_WEIGHT: float = 0.3 # 룰베이스 스코어 비중 ML_ENSEMBLE_ML_WEIGHT: float = 0.7 # ML 스코어 비중 ML_ENSEMBLE_BUY_THRESHOLD: int = 60 # 매수 최소 앙상블 스코어 (0~100) ML_ENSEMBLE_CONFIDENCE_MIN: float = 0.5 # 최소 신뢰도 # API Rate Limiting & Retry Settings (모의투자 API 안전 운영) API_MAX_RETRIES: int = 3 # 최대 재시도 횟수 API_RETRY_DELAY: float = 1.5 # 초기 재시도 대기 시간 (초) API_RETRY_BACKOFF: float = 2.0 # 지수 백오프 배율 API_MIN_REQUEST_INTERVAL: float = 0.5 # 최소 요청 간격 (초) - 초당 2회 권장 API_MAX_CONCURRENT_REQUESTS: int = 2 # 동시 최대 요청 수 API_TIMEOUT: float = 30.0 # API 타임아웃 (초) # Monitoring & Alerting API_ERROR_THRESHOLD: float = 0.3 # 오류율 임계값 (30%) API_MONITORING_WINDOW: int = 100 # 모니터링 윈도우 (최근 N개 요청) class Config: env_file = str(BACKEND_ENV_FILE) if BACKEND_ENV_FILE.exists() else ".env" case_sensitive = True extra = "ignore" # .env 파일의 추가 필드 무시 env_file_encoding = "utf-8" @lru_cache() def get_settings() -> Settings: return Settings() settings = get_settings()