import httpx from config import settings from typing import Dict, Any, List import json import logging logger = logging.getLogger(__name__) class PerplexityClient: """Perplexity AI 클라이언트""" def __init__(self): self.api_key = settings.PERPLEXITY_API_KEY self.model = settings.PERPLEXITY_MODEL self.base_url = "https://api.perplexity.ai" async def analyze( self, system_prompt: str, user_message: str ) -> Dict[str, Any]: """Perplexity를 통한 분석 실행""" try: # API 키 검증 if not self.api_key or not self.api_key.startswith("pplx-"): logger.warning("Invalid Perplexity API key format. Using mock response.") return self._mock_response(user_message) async with httpx.AsyncClient() as client: response = await client.post( f"{self.base_url}/chat/completions", headers={ "Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json" }, json={ "model": self.model, "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_message} ], "max_tokens": settings.MAX_TOKENS, "temperature": settings.TEMPERATURE, "top_p": 0.9, "stream": False }, timeout=30.0 ) response.raise_for_status() result = response.json() # 응답 파싱 content = result.get("choices", [{}])[0].get("message", {}).get("content", "") return { "content": [{"type": "text", "text": content}], "usage": result.get("usage", {}) } except Exception as e: logger.error(f"Perplexity analysis error: {e}") logger.warning("Using mock response due to API error") return self._mock_response(user_message) def _mock_response(self, user_message: str) -> Dict[str, Any]: """Mock 응답 생성 (API 키 없을 때)""" # 분석 타입 감지 if "적정" in user_message and "가격" in user_message: mock_text = """{ "buy_prices": { "aggressive": 50000, "neutral": 52000, "conservative": 54000 }, "sell_prices": { "target1": 56000, "target2": 58000, "target3": 60000 }, "stop_loss": 48000, "strategy": "기술적 분석: 현재 이동평균선 정배열 상태로 상승 추세 지속 중. 지지선 48,000원 유지 시 56,000원까지 상승 가능. 거래량 증가 시 추가 상승 기대.", "confidence": 0.75 }""" elif "시장" in user_message and "분석" in user_message: mock_text = """{ "trend": "UPTREND", "sentiment": "POSITIVE", "recommendation": "BUY", "confidence": 0.7, "analysis": "기술적 분석 결과 상승 추세 지속 중. 거래량 증가와 함께 매수세 강화. 단기 목표가 달성 가능성 높음." }""" elif "규칙" in user_message or "검증" in user_message: mock_text = """{ "decision": "BUY", "confidence": 0.75, "analysis": "현재 시장 상황이 규칙 조건을 만족. 이동평균선 정배열 및 거래량 증가 확인됨. 매수 시점 적절." }""" elif "리스크" in user_message: mock_text = """{ "risk_level": "MEDIUM", "approval": "APPROVED", "confidence": 0.7, "analysis": "포트폴리오 분산 적절. 투자 금액 비중 관리 필요. 손절가 설정 권장." }""" else: mock_text = """{ "analysis": "Mock AI 분석 결과", "confidence": 0.6 }""" return { "content": [{"type": "text", "text": mock_text}], "usage": {"input_tokens": 0, "output_tokens": 0} } async def validate_trading_rule( self, rule: Dict[str, Any], market_data: Dict[str, Any] ) -> Dict[str, Any]: """트레이딩 규칙 검증""" system_prompt = """당신은 주식 트레이딩 규칙을 분석하고 검증하는 전문가입니다. 주어진 규칙과 시장 데이터를 분석하여 다음을 제공하세요: 1. 규칙이 현재 시장 상황에 적합한지 여부 2. 실행 가능성 (0-1 사이의 점수) 3. 리스크 평가 4. 추천 사항 JSON 형식으로 응답하세요.""" user_message = f""" 트레이딩 규칙: {json.dumps(rule, ensure_ascii=False, indent=2)} 시장 데이터: {json.dumps(market_data, ensure_ascii=False, indent=2)} 이 규칙이 현재 시장 상황에서 실행되어야 하는지 분석해주세요. """ response = await self.analyze(system_prompt, user_message) # 텍스트 응답 파싱 for content in response["content"]: if content["type"] == "text": try: # JSON 응답 파싱 시도 return json.loads(content["text"]) except json.JSONDecodeError: # JSON이 아닌 경우 기본 구조로 반환 return { "decision": "HOLD", "confidence": 0.5, "analysis": content["text"] } return { "decision": "HOLD", "confidence": 0.0, "analysis": "분석 실패" } async def analyze_market( self, stock_code: str, market_data: Dict[str, Any], news: List[Dict[str, Any]] = None ) -> Dict[str, Any]: """시장 분석""" system_prompt = """당신은 주식 시장 분석 전문가입니다. 주어진 시장 데이터와 뉴스를 분석하여 다음을 제공하세요: 1. 시장 트렌드 (상승/하락/횡보) 2. 주요 기술적 지표 분석 3. 뉴스 센티먼트 (긍정/부정/중립) 4. 투자 추천 (매수/매도/보유) 5. 신뢰도 (0-1) JSON 형식으로 응답하세요.""" news_text = "" if news: news_text = "\n\n최근 뉴스:\n" + "\n".join([ f"- {item.get('title', '')}: {item.get('content', '')[:200]}" for item in news[:5] ]) user_message = f""" 종목 코드: {stock_code} 시장 데이터: {json.dumps(market_data, ensure_ascii=False, indent=2)} {news_text} 이 종목에 대한 종합적인 시장 분석을 제공해주세요. """ response = await self.analyze(system_prompt, user_message) for content in response["content"]: if content["type"] == "text": try: return json.loads(content["text"]) except json.JSONDecodeError: return { "trend": "SIDEWAYS", "sentiment": "NEUTRAL", "recommendation": "HOLD", "confidence": 0.5, "analysis": content["text"] } return { "trend": "UNKNOWN", "sentiment": "NEUTRAL", "recommendation": "HOLD", "confidence": 0.0 } async def calculate_fair_price( self, stock_code: str, market_data: Dict[str, Any], financial_data: Dict[str, Any] = None ) -> Dict[str, Any]: """AI 기반 적정 매수/매도 가격 산출""" system_prompt = """당신은 주식 가치 평가 및 가격 전략 전문가입니다. 기술적 분석과 시장 데이터를 기반으로 다음을 제공하세요: 1. 적정 매수 가격 (공격적, 중립적, 보수적) 2. 적정 매도 가격 (목표가 1, 2, 3) 3. 손절가 4. 근거 및 전략 5. 신뢰도 (0-1) JSON 형식으로 응답하세요. 예시: { "buy_prices": { "aggressive": 100000, "neutral": 105000, "conservative": 110000 }, "sell_prices": { "target1": 120000, "target2": 130000, "target3": 140000 }, "stop_loss": 95000, "strategy": "기술적 분석 기반 설명...", "confidence": 0.75 }""" user_message = f""" 종목 코드: {stock_code} 시장 데이터: - 현재가: {market_data.get('current_price')} - 고가: {market_data.get('high_price')} - 저가: {market_data.get('low_price')} - 거래량: {market_data.get('volume')} - 기술적 지표: {json.dumps(market_data.get('indicators', {}), ensure_ascii=False)} 이 종목의 적정 매수/매도 가격과 전략을 제시해주세요. """ response = await self.analyze(system_prompt, user_message) for content in response["content"]: if content["type"] == "text": try: return json.loads(content["text"]) except json.JSONDecodeError: return { "buy_prices": {"neutral": market_data.get('current_price', 0)}, "sell_prices": {"target1": market_data.get('current_price', 0) * 1.05}, "stop_loss": market_data.get('current_price', 0) * 0.97, "strategy": content["text"], "confidence": 0.5 } return { "error": "분석 실패", "confidence": 0.0 } async def assess_risk( self, portfolio: Dict[str, Any], trade_plan: Dict[str, Any] ) -> Dict[str, Any]: """리스크 평가""" system_prompt = """당신은 투자 리스크 관리 전문가입니다. 현재 포트폴리오와 계획된 거래를 분석하여 다음을 제공하세요: 1. 리스크 레벨 (LOW/MEDIUM/HIGH/CRITICAL) 2. 주요 리스크 요인 3. 포트폴리오 다양성 평가 4. 추천 사항 5. 승인 여부 (APPROVED/REJECTED) JSON 형식으로 응답하세요.""" user_message = f""" 현재 포트폴리오: {json.dumps(portfolio, ensure_ascii=False, indent=2)} 계획된 거래: {json.dumps(trade_plan, ensure_ascii=False, indent=2)} 이 거래의 리스크를 평가하고 실행 여부를 결정해주세요. """ response = await self.analyze(system_prompt, user_message) for content in response["content"]: if content["type"] == "text": try: return json.loads(content["text"]) except json.JSONDecodeError: return { "risk_level": "MEDIUM", "approval": "HOLD", "confidence": 0.5, "analysis": content["text"] } return { "risk_level": "UNKNOWN", "approval": "REJECTED", "confidence": 0.0 } perplexity_client = PerplexityClient()