MCP 수정중

This commit is contained in:
2026-03-06 14:40:31 +09:00
parent ac98bb856f
commit da5cef1925
4 changed files with 91 additions and 123 deletions

View File

@@ -72,6 +72,9 @@ async def request_mcp_risk_assessment(
- approve=False → 주문 스킵
- MCP 장애 / 타임아웃 → fail-open (approve=True, 매매 중단 방지)
"""
if not settings.MCP_VALIDATION_ENABLED:
return True, "mcp_disabled"
try:
client = get_http_client()
except RuntimeError:

View File

@@ -3,7 +3,6 @@ from typing import List, Optional
from datetime import datetime, date, timedelta
import asyncio
import logging
import httpx
from collections import namedtuple
from app.core.database import get_db, AsyncSessionLocal
@@ -662,72 +661,49 @@ async def execute_auto_trading(db: AsyncSession = Depends(get_db)):
@router.get("/ai/{stock_code}")
async def get_ai_recommendation(stock_code: str):
from app.services.korea_investment_api import korea_investment_api
"""종목 기술적 분석 — AI 미사용, 전략 엔진(RSI/BB/MA/ATR/ADX) 직접 계산."""
from app.services.market_analyzer import analyze_stock_for_batch
try:
price_data = {}
try:
price_data = await korea_investment_api.get_current_price(stock_code)
except Exception as e:
logger.warning(f"Failed to fetch price data locally: {e}")
rec = await analyze_stock_for_batch(stock_code, stock_code)
if rec is None:
raise HTTPException(status_code=404, detail="분석 데이터를 가져올 수 없습니다.")
news_data = []
try:
async with httpx.AsyncClient() as client:
mcp_url = f"{settings.MCP_SERVER_URL}/tools/call"
payload = {
"tool_name": "get_news",
"parameters": {"stock_code": stock_code, "limit": 3}
}
response = await client.post(mcp_url, json=payload, timeout=10.0)
if response.status_code == 200:
result = response.json()
if result.get("status") == "success":
data = result.get("result", {})
if "news" in data:
news_data = data["news"]
else:
news_data = data
except Exception as e:
logger.warning(f"Failed to fetch news via MCP: {e}")
signals = rec.signals or []
score = rec.score or 0
analysis_result = "AI 분석을 수행할 수 없습니다."
try:
async with httpx.AsyncClient() as client:
analyze_url = f"{settings.MCP_SERVER_URL}/analyze"
market_summary = {}
if "output" in price_data:
output = price_data["output"]
market_summary = {
"current_price": output.get("stck_prpr"),
"change_rate": output.get("prdy_ctrt"),
"volume": output.get("acml_vol"),
"high": output.get("stck_hgpr"),
"low": output.get("stck_lwpr")
}
# 전략 신호 기반 요약 문자열 생성
if score >= 3:
summary = f"매수 신호 감지 (점수: {score}). 주요 신호: {', '.join(signals[:3])}"
elif score <= -2:
summary = f"매도/경고 신호 (점수: {score}). 주요 신호: {', '.join(signals[:3])}"
else:
summary = f"보유/관망 (점수: {score}). 신호: {', '.join(signals[:3]) if signals else '없음'}"
payload = {
"analysis_type": "MARKET_ANALYSIS",
"data": {
"stock_code": stock_code,
"market_data": market_summary,
"news": news_data
}
}
response = await client.post(analyze_url, json=payload, timeout=45.0)
if response.status_code == 200:
analysis_res = response.json()
if "result" in analysis_res:
analysis_result = analysis_res["result"]
except Exception as e:
logger.error(f"Failed to fetch AI analysis: {e}")
return {
"stock_code": stock_code,
"analysis": analysis_result,
"news": news_data,
"market_data": price_data.get("output", {})
"analysis": summary,
"signals": signals,
"score": score,
"market_data": {
"current_price": rec.current_price,
"change_rate": rec.change_rate,
"volume": rec.volume,
"rsi": rec.rsi,
"ma5": rec.ma5,
"ma20": rec.ma20,
"ma60": rec.ma60,
"bollinger_upper": rec.bollinger_upper,
"bollinger_lower": rec.bollinger_lower,
"bollinger_middle": rec.bollinger_middle,
"atr_14": rec.atr_14,
"adx_14": rec.adx_14,
"stochastic_k": rec.stochastic_k,
"stochastic_d": rec.stochastic_d,
}
}
except HTTPException:
raise
except Exception as e:
logger.error(f"AI Recommendation error: {e}")
logger.error(f"Strategy analysis error for {stock_code}: {e}")
raise HTTPException(status_code=500, detail=str(e))

View File

@@ -1,6 +1,5 @@
import asyncio
import logging
import httpx
from decimal import Decimal
from datetime import timedelta
from sqlalchemy.ext.asyncio import AsyncSession
@@ -25,58 +24,9 @@ _order_semaphore = asyncio.Semaphore(1)
async def _mcp_validate_buy(stock: dict) -> tuple:
if not settings.MCP_VALIDATION_ENABLED:
return True, "mcp_disabled"
try:
payload = {
"analysis_type": "RULE_VALIDATION",
"data": {
"rule": {
"id": stock.get("rule_id"),
"name": stock.get("rule_name", "auto_trade"),
},
"market_data": {
"stock_code": stock.get("stock_code"),
"stock_name": stock.get("stock_name"),
"current_price": stock.get("current_price"),
"signals": stock.get("signals", []),
"score": stock.get("score"),
"ml_signal": stock.get("ml_signal"),
"ml_confidence": stock.get("ml_confidence"),
"ma20": stock.get("ma20"),
"ma60": stock.get("ma60"),
}
}
}
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.post(
f"{settings.MCP_SERVER_URL}/analyze",
json=payload
)
resp.raise_for_status()
data = resp.json()
result = data.get("result", {})
decision = result.get("decision", "HOLD")
confidence = float(result.get("confidence", 0.0))
if decision == "BUY" and confidence >= settings.MCP_CONFIDENCE_THRESHOLD:
return True, f"mcp_approved(conf={confidence:.2f})"
else:
return False, f"mcp_rejected(decision={decision}, conf={confidence:.2f})"
except httpx.HTTPStatusError as e:
if e.response.status_code == 500:
logger.warning(f"⚠️ MCP 서버 500 에러 — 룰 기반으로 우회: {e}")
return True, "mcp_500_fallback_to_rules"
logger.warning(f"⚠️ MCP 검증 실패 — 매매 차단: {e}")
return False, "mcp_unavailable"
except Exception as e:
logger.warning(f"⚠️ MCP 검증 실패 — 매매 차단: {e}")
return False, "mcp_unavailable"
# 전략 엔진(RSI/BB/MA/ATR 등)이 이미 신호를 검증하므로 AI 호출 불필요.
# RISK_ASSESSMENT는 매매 직전 mcp.py에서 별도 수행.
return True, "strategy_validated"
async def _execute_buy(