- 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메뉴: 실거래 / 과거기록 / 종목조회
101 lines
2.9 KiB
Python
101 lines
2.9 KiB
Python
from __future__ import annotations
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
from app.core.config import settings
|
|
from app.core.database import init_db
|
|
from app.core.logger import setup_logger
|
|
from app.core.auth import token_manager
|
|
from app.engine.scheduler import start_scheduler, stop_scheduler
|
|
from app.services.market_data import market_data_service
|
|
from app.services.trading import trading_service
|
|
from app.services.account import account_service
|
|
from app.services.realtime import realtime_service
|
|
|
|
from app.routers import stocks, trading, strategies, websocket, dashboard
|
|
|
|
logger = setup_logger("main", level=settings.app.log_level)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
logger.info("=== Stock Automation 시작 ===")
|
|
logger.info("서버 모드: %s", settings.kis.server_mode)
|
|
|
|
init_db()
|
|
logger.info("데이터베이스 초기화 완료")
|
|
|
|
try:
|
|
await token_manager.get_access_token()
|
|
if token_manager.is_authenticated:
|
|
logger.info("KIS 인증 완료")
|
|
else:
|
|
logger.warning(
|
|
"KIS 인증 실패 - API 키를 확인하세요 (.env 파일). "
|
|
"UI는 표시되지만 시세 조회/매매 기능은 작동하지 않습니다."
|
|
)
|
|
except Exception as e:
|
|
logger.warning("KIS 인증 오류: %s", e)
|
|
|
|
if token_manager.is_authenticated:
|
|
start_scheduler()
|
|
try:
|
|
await realtime_service.connect()
|
|
logger.info("KIS WebSocket 연결 완료")
|
|
except Exception as e:
|
|
logger.warning("KIS WebSocket 연결 실패: %s", e)
|
|
else:
|
|
logger.info("인증 실패 - 스케줄러 미시작 (인증 후 수동 시작 가능)")
|
|
|
|
yield
|
|
|
|
logger.info("=== Stock Automation 종료 ===")
|
|
stop_scheduler()
|
|
await realtime_service.disconnect()
|
|
await market_data_service.close()
|
|
await trading_service.close()
|
|
await account_service.close()
|
|
await token_manager.close()
|
|
|
|
|
|
app = FastAPI(
|
|
title="Stock Automation",
|
|
description="한국투자증권 API 기반 자동매매 시스템",
|
|
version="0.1.0",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
app.include_router(stocks.router)
|
|
app.include_router(trading.router)
|
|
app.include_router(strategies.router)
|
|
app.include_router(websocket.router)
|
|
app.include_router(dashboard.router)
|
|
|
|
templates = Jinja2Templates(directory="app/templates")
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse)
|
|
async def index(request: Request):
|
|
return templates.TemplateResponse(request, "index.html")
|
|
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
return {"status": "ok", "mode": settings.kis.server_mode}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
|
|
uvicorn.run(
|
|
"app.main:app",
|
|
host=settings.app.host,
|
|
port=settings.app.port,
|
|
reload=settings.app.debug,
|
|
)
|