diff --git a/agent-office/app/agents/stock.py b/agent-office/app/agents/stock.py new file mode 100644 index 0000000..6f8c441 --- /dev/null +++ b/agent-office/app/agents/stock.py @@ -0,0 +1,95 @@ +import asyncio +from typing import Optional + +from .base import BaseAgent +from ..db import create_task, update_task_status, get_agent_config, add_log +from .. import service_proxy + +class StockAgent(BaseAgent): + agent_id = "stock" + display_name = "주식 트레이더" + + async def on_schedule(self) -> None: + if self.state not in ("idle", "break"): + return + + task_id = create_task(self.agent_id, "news_summary", {"limit": 15}) + await self.transition("working", "뉴스 수집 중...", task_id) + + try: + news = await service_proxy.fetch_stock_news(limit=15) + indices = await service_proxy.fetch_stock_indices() + + summary = self._format_news_summary(news, indices) + + update_task_status(task_id, "succeeded", { + "summary": summary, + "news_count": len(news) if isinstance(news, list) else 0, + }) + + await self.transition("reporting", "뉴스 요약 전송 중...") + + from ..telegram_bot import send_stock_summary + await send_stock_summary(summary) + + await self.transition("idle", "뉴스 요약 완료") + + except Exception as e: + add_log(self.agent_id, f"News summary failed: {e}", "error", task_id) + update_task_status(task_id, "failed", {"error": str(e)}) + await self.transition("idle", f"오류: {e}") + + async def on_command(self, command: str, params: dict) -> dict: + if command == "fetch_news": + await self.on_schedule() + return {"ok": True, "message": "뉴스 수집 시작"} + + if command == "add_alert": + config = get_agent_config(self.agent_id) + alerts = config["custom_config"].get("alerts", []) + alerts.append({ + "symbol": params["symbol"], + "name": params.get("name", params["symbol"]), + "target_price": params["target_price"], + "direction": params.get("direction", "above"), + }) + from ..db import update_agent_config + update_agent_config(self.agent_id, custom_config={**config["custom_config"], "alerts": alerts}) + return {"ok": True, "message": f"알람 추가: {params['symbol']}"} + + if command == "list_alerts": + config = get_agent_config(self.agent_id) + alerts = config["custom_config"].get("alerts", []) + return {"ok": True, "alerts": alerts} + + return {"ok": False, "message": f"Unknown command: {command}"} + + async def on_approval(self, task_id: str, approved: bool, feedback: str = "") -> None: + pass + + def _format_news_summary(self, news, indices) -> str: + lines = ["📈 [주식 에이전트] 아침 뉴스 요약", "━" * 20] + + if isinstance(news, list): + for item in news[:10]: + title = item.get("title", "") + if title: + lines.append(f"• {title}") + elif isinstance(news, dict) and "articles" in news: + for item in news["articles"][:10]: + title = item.get("title", "") + if title: + lines.append(f"• {title}") + + if indices: + lines.append("") + lines.append("📊 주요 지수") + if isinstance(indices, dict): + for key, val in indices.items(): + if isinstance(val, dict): + name = val.get("name", key) + price = val.get("price", "") + change = val.get("change", "") + lines.append(f"{name}: {price} ({change})") + + return "\n".join(lines)