from datetime import datetime import time import os from dotenv import load_dotenv from modules.services.kis import KISClient class MacroAnalyzer: """ KIS API를 활용한 거시경제(시장 지수) 분석 모듈 yfinance 대신 한국투자증권 API를 사용하여 안정적인 KOSPI, KOSDAQ 데이터를 수집함. """ @staticmethod def get_macro_status(kis_client): """ 시장 주요 지수(KOSPI, KOSDAQ)를 조회하여 시장 위험도를 평가함. Args: kis_client (KISClient): 인증된 KIS API 클라이언트 인스턴스 Returns: dict: 시장 상태 (SAFE, CAUTION, DANGER) 및 지표 데이터 """ indicators = { "KOSPI": "0001", "KOSDAQ": "1001" } results = {} risk_score = 0 print("🌍 [Macro] Fetching market indices via KIS API...") for name, code in indicators.items(): data = kis_client.get_current_index(code) time.sleep(0.6) # Rate Limit 방지 (초당 2회 제한) if data: price = data['price'] change = data['change'] results[name] = {"price": price, "change": change} print(f" - {name}: {price} ({change}%)") # 리스크 평가 로직 (단순화: 2% 이상 폭락 장이면 위험) if change <= -2.0: risk_score += 2 # 패닉 상태 elif change <= -1.0: risk_score += 1 # 주의 상태 else: results[name] = {"price": 0, "change": 0} # [신규] 시장 스트레스 지수(MSI) 추가 time.sleep(0.6) # MSI 계산 전 추가 대기 kospi_stress = MacroAnalyzer.calculate_stress_index(kis_client, "0001") results['MSI'] = kospi_stress print(f" - Market Stress Index: {kospi_stress}") if kospi_stress >= 50: risk_score += 2 # 매우 위험 elif kospi_stress >= 30: risk_score += 1 # 위험 # 시장 상태 정의 status = "SAFE" if risk_score >= 2: status = "DANGER" # 매수 중단 권장 elif risk_score >= 1: status = "CAUTION" # 보수적 매매 return { "status": status, "risk_score": risk_score, "indicators": results } @staticmethod def calculate_stress_index(kis_client, market_code="0001"): """ 시장 스트레스 지수(MSI) 계산 - 0~100 사이의 값 (높을수록 위험) - 요소: 변동성(Volatility), 추세 이격도(MA Divergence) """ import numpy as np # 일봉 데이터 조회 (약 3개월치 = 60일 이상) prices = kis_client.get_daily_index_price(market_code, period="D") if not prices or len(prices) < 20: return 0 prices = np.array(prices) # 1. 역사적 변동성 (20일) # 로그 수익률 계산 returns = np.diff(np.log(prices)) # 연환산 변동성 (Trading days = 252) volatility = np.std(returns[-20:]) * np.sqrt(252) * 100 # 2. 이동평균 이격도 ma20 = np.mean(prices[-20:]) current_price = prices[-1] disparity = (current_price - ma20) / ma20 * 100 # 3. 스트레스 점수 산출 # 변동성이 20% 넘어가면 위험, 이격도가 -5% 이하면 위험 stress_score = 0 # 변동성 기여 (평소 10~15%, 30% 이상 공포) # 10 이하면 0점, 40 이상이면 60점 만점 v_score = min(max((volatility - 10) * 2, 0), 60) # 하락 추세 기여 (-10% 이격이면 +40점) d_score = 0 if disparity < 0: d_score = min(abs(disparity) * 4, 40) total_stress = v_score + d_score return round(total_stress, 2) if __name__ == "__main__": # 테스트를 위한 코드 load_dotenv() # 환경변수 로딩 및 클라이언트 초기화 if os.getenv("KIS_ENV_TYPE") == "real": app_key = os.getenv("KIS_REAL_APP_KEY") app_secret = os.getenv("KIS_REAL_APP_SECRET") account = os.getenv("KIS_REAL_ACCOUNT") is_virtual = False else: app_key = os.getenv("KIS_VIRTUAL_APP_KEY") app_secret = os.getenv("KIS_VIRTUAL_APP_SECRET") account = os.getenv("KIS_VIRTUAL_ACCOUNT") is_virtual = True kis = KISClient(app_key, app_secret, account, is_virtual) # 토큰 발급 (필요 시) kis.ensure_token() # 분석 실행 report = MacroAnalyzer.get_macro_status(kis) print("\n📊 [Macro Report]") print(f"Status: {report['status']}") print(f"Data: {report['indicators']}")