수정본
Some checks failed
CI/CD Pipeline / test-backend (push) Has been cancelled
CI/CD Pipeline / test-frontend (push) Has been cancelled
CI/CD Pipeline / build-and-push (push) Has been cancelled

This commit is contained in:
2026-03-27 14:47:49 +09:00
parent 844ee8944f
commit c818810667
25 changed files with 2708 additions and 266 deletions

View File

@@ -218,5 +218,104 @@ class GeminiClient:
{"error": "Failed to generate journal"}
)
async def analyze_batch_stocks(
self,
stocks: List[Dict[str, Any]],
) -> Dict[str, Any]:
"""장 전 배치 분석 — 10종목을 한 번에 받아 각각 매수적합/부적합 판정.
max_tokens=2048 (10종목 응답이므로 넉넉하게).
입력 stocks: [{stock_code, stock_name, current_price, rsi, bb_position,
volume_ratio, score, news_titles}, ...]
"""
system_prompt = """주식 스윙 트레이딩 분석가. 종목 리스트를 분석하여 JSON 배열로 응답.
각 종목: stock_code, suitable(true/false), confidence(0-1), reason(한줄), risk_factors(배열).
JSON 외 텍스트 금지."""
user_message = f"종목리스트:{_dumps(stocks)}"
response = await self.analyze(system_prompt, user_message, max_tokens=2048)
return self._parse_batch_response(response)
def _parse_batch_response(self, response: Dict[str, Any]) -> Dict[str, Any]:
"""배치 분석 응답 파싱 — JSON 배열 또는 객체 모두 처리.
1) 응답 텍스트에서 JSON 배열([...]) 파싱 시도
2) JSON 객체({...}) 파싱 시도 (AI가 {stocks: [...]} 형태로 응답할 수 있음)
3) 모두 실패하면 빈 결과 반환
"""
for content in response["content"]:
if content["type"] == "text":
text = content["text"]
# 배열 형태 시도
try:
arr_start = text.find('[')
arr_end = text.rfind(']') + 1
if arr_start != -1 and arr_end > arr_start:
parsed = json.loads(text[arr_start:arr_end])
if isinstance(parsed, list):
return {"stocks": parsed}
except (json.JSONDecodeError, Exception):
pass
# 객체 형태 시도
try:
obj_start = text.find('{')
obj_end = text.rfind('}') + 1
if obj_start != -1 and obj_end > obj_start:
return json.loads(text[obj_start:obj_end])
except (json.JSONDecodeError, Exception):
pass
return {"stocks": [], "analysis": text}
return {"stocks": []}
async def check_news_sentiment(
self,
stock_code: str,
stock_name: str,
news_titles: List[str],
) -> Dict[str, Any]:
"""장중 뉴스 감성 분석. max_tokens=256 (짧은 응답)."""
system_prompt = """주식 뉴스 감성 분석. JSON만 출력.
필수: sentiment(POSITIVE/NEGATIVE/NEUTRAL), severity(LOW/MEDIUM/HIGH), action(HOLD/SELL/WATCH), reason(한줄).
JSON 외 텍스트 금지."""
user_message = f"종목:{stock_code}({stock_name})\n뉴스제목:{_dumps(news_titles)}"
response = await self.analyze(system_prompt, user_message, max_tokens=256)
return self._parse_json_response(
response,
{"sentiment": "NEUTRAL", "severity": "LOW", "action": "HOLD", "reason": "분석 실패"}
)
async def review_daily_trades(
self,
trades_summary: Dict[str, Any],
market_summary: Dict[str, Any],
) -> Dict[str, Any]:
"""장 후 당일 매매 복기.
max_tokens=1024.
trades_summary: {total_trades, wins, losses, pnl, strategies_used, notable_trades}
market_summary: {kospi_change, regime, top_movers}
"""
system_prompt = """주식 매매 복기 전문가. 당일 매매를 분석하여 JSON 응답.
필수: overall_grade(A/B/C/D/F), good_points(배열), bad_points(배열), improvements(배열), strategy_drift(bool), drift_detail(있으면), tomorrow_plan(한줄).
JSON 외 텍스트 금지."""
user_message = f"매매요약:{_dumps(trades_summary)}\n시장요약:{_dumps(market_summary)}"
response = await self.analyze(system_prompt, user_message, max_tokens=1024)
return self._parse_json_response(
response,
{
"overall_grade": "C",
"good_points": [],
"bad_points": [],
"improvements": [],
"strategy_drift": False,
"tomorrow_plan": "분석 실패",
}
)
gemini_client = GeminiClient()