Files
auto-stock/mcp-server/perplexity_client.py
2025-11-14 11:05:31 +09:00

334 lines
12 KiB
Python

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()