Files
stockautomtion/app/routers/dashboard.py
gerd 182c2b38e6 feat: config.yaml 기반 설정 마이그레이션 및 대시보드/종목조회 기능 추가
- config.yaml에서 직접 KIS 설정 관리 (BaseSettings 제거)
- 대시보드 KIS 계좌 잔고 실시간 연동, 로컬 DB 폴백
- 종목 조회 페이지: 시세/호가/일봉차트 (canvas) 구현
- 호가 REST API 엔드포인트 (/api/stocks/{code}/orderbook) 추가
- 토큰 캐시(.auth_cache.json) 저장/복원, 1분 재시도 제한
- KIS WebSocket 자동 연결 + 재연결 로직
- httpx 타임아웃 10초→30초, 라우터 타임아웃 핸들링 (504)
- rate limit: requests_per_second 2.0 (모의투자 초당 2건)
- 사이드바 3메뉴: 실거래 / 과거기록 / 종목조회
2026-07-17 15:25:34 +09:00

105 lines
3.6 KiB
Python

from __future__ import annotations
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.core.auth import token_manager
from app.core.database import get_db
from app.models.stock import Holding, Strategy, Trade, Stock
from app.engine.scheduler import scheduler
from app.services.account import account_service
from app.services.realtime import realtime_service
router = APIRouter(prefix="/api/dashboard", tags=["dashboard"])
@router.get("/")
async def dashboard(db: Session = Depends(get_db)) -> dict:
recent_trades = db.query(Trade).order_by(Trade.created_at.desc()).limit(20).all()
active_strategies = db.query(Strategy).filter(Strategy.is_active == True).all()
active_stocks = db.query(Stock).filter(Stock.is_active == True).all()
holdings = []
total_profit = 0.0
total_invested = 0.0
total_evaluated = 0.0
if token_manager.is_authenticated:
try:
balance = await account_service.get_balance()
for s in balance.get("stocks", []):
holdings.append({
"stock_code": s["stock_code"],
"stock_name": s["stock_name"],
"qty": s["qty"],
"avg_price": s["avg_price"],
"current_price": s["current_price"],
"profit": s["profit"],
"profit_rate": s["profit_rate"],
})
total_invested += s.get("buy_amount", s["avg_price"] * s["qty"])
total_evaluated += s.get("eval_amount", s["current_price"] * s["qty"])
total_profit += s["profit"]
except Exception:
pass
if not holdings:
local = db.query(Holding).all()
for h in local:
holdings.append({
"stock_code": h.stock_code,
"stock_name": h.stock_name,
"qty": h.qty,
"avg_price": h.avg_price,
"current_price": h.current_price,
"profit": h.profit,
"profit_rate": h.profit_rate,
})
total_invested += h.avg_price * h.qty
total_evaluated += h.current_price * h.qty
total_profit += h.profit
jobs = []
if scheduler.running:
for job in scheduler.get_jobs():
jobs.append({
"id": job.id,
"name": job.name,
"next_run": str(job.next_run_time) if job.next_run_time else None,
})
return {
"authenticated": token_manager.is_authenticated,
"summary": {
"total_holdings": len(holdings),
"total_profit": total_profit,
"total_invested": total_invested,
"total_evaluated": total_evaluated,
"profit_rate": (total_profit / total_invested * 100) if total_invested > 0 else 0,
"active_strategies": len(active_strategies),
"active_stocks": len(active_stocks),
},
"holdings": holdings,
"recent_trades": [
{
"id": t.id,
"stock_code": t.stock_code,
"side": t.side,
"qty": t.qty,
"price": t.price,
"status": t.status,
"created_at": t.created_at.isoformat() if t.created_at else None,
}
for t in recent_trades
],
"scheduler_jobs": jobs,
}
@router.get("/status")
def get_status() -> dict:
return {
"authenticated": token_manager.is_authenticated,
"realtime_connected": realtime_service.is_connected,
}