Files
auto-stock/backend/app/core/config.py
2026-02-26 11:19:23 +09:00

290 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
애플리케이션 설정 모듈
- 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 = ""
# WebSocket 접속키 (수동 지정 시 우선, 비어 있으면 자동 발급)
KIS_WS_APPROVAL_KEY: str = ""
# KIS WebSocket URL (.env에서 오버라이드 가능)
# KIS 공식 실시간 시세 WebSocket 엔드포인트
# 경로: /websocket (또는 /cgi-md5 in some cases)
KIS_WS_REAL_URL: str = "wss://openapi.koreainvestment.com:21000/websocket"
KIS_WS_VIRTUAL_URL: str = "wss://openapivts.koreainvestment.com:29443/websocket"
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
ANALYSIS_STOCK_COUNT: int = 150 # 배치 분석 종목 수 (.env로 조정 가능)
AUTO_TRADE_TOP_N_PER_RULE: int = 2 # 규칙당 자동매매 실행 최대 종목 수
# 포지션 규칙 (섹션 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 = 4 # 동시 최대 요청 수 (batch와 UI 요청 동시 처리)
API_TIMEOUT: float = 30.0 # API 타임아웃 (초)
# Monitoring & Alerting
API_ERROR_THRESHOLD: float = 0.3 # 오류율 임계값 (30%)
API_MONITORING_WINDOW: int = 100 # 모니터링 윈도우 (최근 N개 요청)
# ═════════════════════════════════════════════════════════════════════════════
# 데이트레이딩 세부 파라미터 (day_trading_strategy.py)
# ═════════════════════════════════════════════════════════════════════════════
DAY_TRADE_MAX_CAPITAL_RATIO: float = 0.10 # 종목당 최대 10% 자본
DAY_TRADE_MAX_RATIO_DT: float = 0.25 # 전체 데이트레이딩 최대 25%
DAY_TRADE_RSI_BREAKEVEN_THRESHOLD: float = 40.0 # RSI_BB 진입 기준 (5분봉 RSI ≤ 40)
DAY_TRADE_ORB_STOP_LOSS_PCT: float = 0.02 # ORB 손절 -2%
DAY_TRADE_REVERSION_STOP_LOSS_PCT: float = 0.025 # VWAP/RSI_BB 손절 -2.5%
DAY_TRADE_TRAILING_TRIGGER: float = 0.015 # 트레일링 발동 +1.5%
DAY_TRADE_TRAILING_ATR_MULT: float = 1.0 # 트레일링 ATR 배수
DAY_TRADE_ORB_BREAKOUT_MARGIN: float = 0.002 # ORB 돌파 최소 여유 +0.2%
DAY_TRADE_COST_PCT: float = 0.005 # 수수료+슬리피지 합산 (매수+매도)
DAY_TRADE_ENTRY_CUTOFF_HOUR: int = 15 # 신규 진입 마감 시간 (15시)
DAY_TRADE_ENTRY_CUTOFF_MIN: int = 0 # 신규 진입 마감 분
DAY_TRADE_EOD_HOUR: int = 15 # EOD 강제 청산 시작 시간
DAY_TRADE_EOD_MIN: int = 20 # EOD 강제 청산 시작 분
# ═════════════════════════════════════════════════════════════════════════════
# 단기 전략 (short_term_strategy.py)
# ═════════════════════════════════════════════════════════════════════════════
SHORT_TERM_MIN_ATR_RATIO: float = 0.008 # ATR/close ≥ 0.8% : 거래비용 커버
SHORT_TERM_RSI_LOOKBACK: int = 3 # RSI 반등 확인용 과거 봉 수
SHORT_TERM_VOLUME_MULTIPLIER: float = 1.5 # 거래량 필터 배수 (1.5배 이상)
# ═════════════════════════════════════════════════════════════════════════════
# 평균회귀 전략 (mean_reversion_strategy.py)
# ═════════════════════════════════════════════════════════════════════════════
MEAN_REVERSION_RSI_THRESHOLD: float = 30.0 # RSI 과매도 기준 (≤30)
MEAN_REVERSION_MULTIPLIER_STRONG: float = 1.50 # 강한 신호 배수 (+50%)
MEAN_REVERSION_MULTIPLIER_NORMAL: float = 1.25 # 중간 신호 배수 (+25%)
MEAN_REVERSION_MULTIPLIER_WEAK: float = 1.00 # 약한 신호 배수 (기본값)
# ═════════════════════════════════════════════════════════════════════════════
# KIS WebSocket & 실시간 스트림 (kis_realtime_stream.py)
# ═════════════════════════════════════════════════════════════════════════════
KIS_WS_ENABLED: bool = True # WebSocket 실시간 스트림 활성화 (True) / 비활성화 (False)
KIS_SYNC_INTERVAL: int = 10 # 구독 목록 동기화 주기 (초)
KIS_RECONNECT_WAIT: int = 5 # 재연결 대기 시간 (초)
KIS_APPROVAL_KEY_TTL: int = 82800 # approval_key 캐시 유효시간 (초, 23시간)
# ═════════════════════════════════════════════════════════════════════════════
# 감시 목록 (watchlist.py)
# ═════════════════════════════════════════════════════════════════════════════
WATCHLIST_INIT_SIZE: int = 50 # 아침 배치 초기화 종목 수
WATCHLIST_MAX_SIZE: int = 100 # 최대 관리 종목 수
WATCHLIST_ENTRY_SCORE: int = 60 # 장중 신규 진입 기준 점수
WATCHLIST_EXIT_SCORE: int = 20 # 장중 제거 기준 점수
# ═════════════════════════════════════════════════════════════════════════════
# 기술적 지표 임계값 (technical_indicators.py)
# ═════════════════════════════════════════════════════════════════════════════
RSI_OVERSOLD_LEVEL: float = 30.0 # RSI 과매도 수준
RSI_OVERBOUGHT_LEVEL: float = 70.0 # RSI 과매수 수준
RSI_RECOVERY_RANGE_MIN: float = 30.0 # RSI 회복 범위 최소값
RSI_RECOVERY_RANGE_MAX: float = 45.0 # RSI 회복 범위 최대값
RSI_STABLE_RANGE_MIN: float = 45.0 # RSI 안정 범위 최소값
RSI_STABLE_RANGE_MAX: float = 60.0 # RSI 안정 범위 최대값
BOLLINGER_PERIOD: int = 20 # 볼린저 밴드 기간 (20일)
BOLLINGER_STDDEV: float = 2.0 # 볼린저 밴드 표준편차 배수
MA_SHORT_PERIOD: int = 5 # 단기 이동평균 기간 (5일)
MA_MEDIUM_PERIOD: int = 20 # 중기 이동평균 기간 (20일)
MA_LONG_PERIOD: int = 60 # 장기 이동평균 기간 (60일)
RSI_PERIOD: int = 14 # RSI 계산 기간
ATR_PERIOD: int = 14 # ATR 계산 기간
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()