Files
auto-stock/mcp-server/main.py
2026-02-22 17:23:20 +09:00

244 lines
7.5 KiB
Python

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Dict, Any, List
import logging
from config import settings
from claude_client import claude_client
from mcp_tools import mcp_tools
# AI_PROVIDER에 따라 클라이언트 동적 선택
if settings.AI_PROVIDER == "gemini":
from gemini_client import gemini_client
ai_client = gemini_client
else:
ai_client = claude_client
# 로깅 설정
logging.basicConfig(
level=getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = FastAPI(
title="Auto Stock Trading MCP Server",
description="Model Context Protocol Server for Stock Trading with Claude AI",
version="1.0.0"
)
# CORS 설정
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class ToolCallRequest(BaseModel):
tool_name: str
parameters: Dict[str, Any]
class AnalysisRequest(BaseModel):
analysis_type: str # RULE_VALIDATION, MARKET_ANALYSIS, RISK_ASSESSMENT
data: Dict[str, Any]
@app.get("/")
async def root():
return {
"message": "MCP Server for Auto Stock Trading",
"version": "1.0.0",
"status": "running"
}
@app.get("/health")
async def health_check():
return {
"status": "healthy",
"ai_provider": settings.AI_PROVIDER,
"claude_configured": bool(settings.ANTHROPIC_API_KEY),
"gemini_configured": bool(settings.GEMINI_API_KEY),
}
@app.get("/tools")
async def list_tools():
"""사용 가능한 도구 목록"""
return {
"tools": mcp_tools.get_tool_definitions()
}
@app.post("/tools/call")
async def call_tool(request: ToolCallRequest):
"""MCP 도구 호출"""
try:
result = await mcp_tools.call_tool(request.tool_name, request.parameters)
return result
except Exception as e:
logger.error(f"Tool call error: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/analyze")
async def analyze(request: AnalysisRequest):
"""Claude를 통한 분석"""
try:
analysis_type = request.analysis_type
data = request.data
if analysis_type == "RULE_VALIDATION":
# 규칙 검증
rule = data.get("rule")
market_data = data.get("market_data")
if not rule or not market_data:
raise HTTPException(
status_code=400,
detail="rule and market_data are required"
)
# 뉴스 정보 추가 수집
stock_code = market_data.get("stock_code")
news_data = []
if stock_code:
try:
news_result = await mcp_tools.get_news(stock_code, limit=settings.NEWS_LIMIT)
if "news" in news_result:
news_data = news_result["news"]
except Exception as e:
logger.warning(f"Failed to fetch news for validation: {e}")
result = await ai_client.validate_trading_rule(rule, market_data, news_data)
return {
"analysis_type": analysis_type,
"result": result
}
elif analysis_type == "MARKET_ANALYSIS":
# 시장 분석
stock_code = data.get("stock_code")
market_data = data.get("market_data")
news = data.get("news", [])
if not stock_code or not market_data:
raise HTTPException(
status_code=400,
detail="stock_code and market_data are required"
)
result = await ai_client.analyze_market(stock_code, market_data, news)
return {
"analysis_type": analysis_type,
"result": result
}
elif analysis_type == "RISK_ASSESSMENT":
# 리스크 평가
portfolio = data.get("portfolio")
trade_plan = data.get("trade_plan")
if not portfolio or not trade_plan:
raise HTTPException(
status_code=400,
detail="portfolio and trade_plan are required"
)
result = await ai_client.assess_risk(portfolio, trade_plan)
return {
"analysis_type": analysis_type,
"result": result
}
elif analysis_type == "FAIR_PRICE":
# 적정가 분석
stock_code = data.get("stock_code")
market_data = data.get("market_data")
financial_data = data.get("financial_data")
if not stock_code or not market_data:
raise HTTPException(
status_code=400,
detail="stock_code and market_data are required"
)
result = await ai_client.calculate_fair_price(
stock_code, market_data, financial_data
)
return {
"analysis_type": analysis_type,
"result": result
}
elif analysis_type == "RULE_OPTIMIZATION":
# 트레이딩 규칙 최적화/생성
stock_code = data.get("stock_code")
market_data = data.get("market_data")
if not stock_code or not market_data:
raise HTTPException(
status_code=400,
detail="stock_code and market_data are required"
)
# 뉴스 정보 추가 수집
news_data = []
try:
news_result = await mcp_tools.get_news(stock_code, limit=settings.NEWS_LIMIT)
if "news" in news_result:
news_data = news_result["news"]
except Exception as e:
logger.warning(f"Failed to fetch news for optimization: {e}")
result = await ai_client.optimize_trading_rules(stock_code, market_data, news_data)
return {
"analysis_type": analysis_type,
"result": result
}
elif analysis_type == "GENERATE_JOURNAL":
# 매매일지 생성
trade_info = data.get("trade_info")
market_data = data.get("market_data")
if not trade_info or not market_data:
raise HTTPException(
status_code=400,
detail="trade_info and market_data are required"
)
result = await ai_client.generate_trade_journal(trade_info, market_data)
return {
"analysis_type": analysis_type,
"result": result
}
else:
raise HTTPException(
status_code=400,
detail=f"Unknown analysis type: {analysis_type}"
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Analysis error: {e}")
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host=settings.MCP_SERVER_HOST,
port=settings.MCP_SERVER_PORT,
reload=True
)