From 6d673e06ce740a5a2d8f3c62a7397042b19cc27d Mon Sep 17 00:00:00 2001 From: LGram16 Date: Mon, 2 Feb 2026 23:13:28 +0900 Subject: [PATCH] =?UTF-8?q?=EB=AC=B8=EC=84=9C=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 47 ++++++------ backend/ReadMe.md | 134 ++++++++++++++++++++++++++++++---- backend/TODO.md | 90 +++++++++++++++++++++++ backend/api.md | 175 ++++++++++++++++++++++++++++++--------------- backend/models.md | 93 +++++++++++++++++++----- backend/tables.md | 165 ++++++++++++++++++++++++------------------ pages/Settings.tsx | 4 ++ types.ts | 2 + 8 files changed, 531 insertions(+), 179 deletions(-) create mode 100644 backend/TODO.md diff --git a/Dockerfile b/Dockerfile index 3c5b203..f819cf1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,31 +1,34 @@ -# 1단계: 빌드 (Node.js) -FROM node:20-alpine AS build -WORKDIR /app +# 1. Frontend Build Stage +FROM node:20-alpine AS frontend-build +WORKDIR /app/frontend COPY package*.json ./ -RUN rm -f package-lock.json && npm install +RUN npm install COPY . . RUN npm run build -# 2단계: 실행 (Nginx) -FROM nginx:alpine -WORKDIR /usr/share/nginx/html +# 2. Backend & Serving Stage +FROM python:3.9-slim -# 기존 기본 파일 제거 및 빌드 결과물 복사 -RUN rm -rf ./* -COPY --from=build /app/dist . +# Set working directory +WORKDIR /app -# React Router SPA 라우팅을 위한 Nginx 설정 적용 -RUN echo 'server { \ - listen 80; \ - location / { \ - root /usr/share/nginx/html; \ - index index.html index.htm; \ - try_files $uri $uri/ /index.html; \ - } \ -}' > /etc/nginx/conf.d/default.conf +# Install system dependencies (if needed for TA-Lib or others) +# RUN apt-get update && apt-get install -y gcc ... -# 환경변수 포트 노출 +# Copy backend requirements +COPY ./backend/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy backend code +COPY ./backend ./backend + +# Copy frontend build artifacts to backend static folder +COPY --from=frontend-build /app/frontend/dist ./backend/static + +# Environment variables +ENV PORT=80 EXPOSE 80 -# Nginx 서버 실행 -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file +# Run FastAPI server +# Assuming main.py is in backend folder and app object is named 'app' +CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "80"] \ No newline at end of file diff --git a/backend/ReadMe.md b/backend/ReadMe.md index 02c5f0c..189bc00 100644 --- a/backend/ReadMe.md +++ b/backend/ReadMe.md @@ -1,19 +1,123 @@ -# BatchuKis 백엔드 독립 실행 엔진 및 API 통합 사양서 (v1.6) +# BatchuKis 백엔드 독립 실행 엔진 및 API 통합 사양서 (v1.7) -본 문서는 BatchuKis 플랫폼의 백엔드 시스템 아키텍처 및 독립 실행형 매매 엔진 로직을 정의합니다. +본 문서는 BatchuKis 플랫폼의 백엔드 시스템 아키텍처, 독립 실행형 매매 엔진 로직 및 개발 환경을 정의합니다. -## 1. 시스템 아키텍처 +--- -### 1.1 Headless Execution Engine -1. **Batch Engine**: 매 1분마다 `auto_trade_configs`를 스캔하여 예약된 시각에 주문 실행. -2. **Monitoring Engine**: WebSocket 시세를 수신하여 `reserved_orders` 조건 감시 및 자동 매매. -3. **Market Index Collector**: - - **조회**: 매 5분마다 주요 시장 지수(KOSPI, KOSDAQ, NASDAQ, S&P500, USD/KRW) 수신. - - **기록**: 1시간마다 해당 시점의 최종 데이터를 `market_index_history` 테이블에 기록(Upsert). - - **프론트 연동**: 프론트엔드는 DB에 저장된 최신 데이터를 5분 단위로 폴링하여 대시보드 업데이트. -4. **AI Proxy**: API 보안을 위해 AI 분석 및 뉴스 요청 중계. +## 1. 개발 및 실행 환경 (Development Environment) -## 2. 상세 명세 가이드 -- **DB 스키마**: [tables.md](./tables.md) 참조. -- **API 엔드포인트**: [api.md](./api.md) 참조. -- **데이터 구조(JSON)**: [models.md](./models.md) 참조. +- **Runtime**: Docker Container (Python 3.8+ Base Image) +- **Deployment**: Dokploy (Self-hosted PaaS) +- **Framework**: FastAPI (비동기 처리) or Flask +- **Database**: SQLite (Volume Mounting for Persistence) +- **Frontend Hosting**: Python 백엔드가 React 빌드 결과물(`dist`)을 정적 파일로 서빙 (Single Container Monolith). + +### 1.1 Production Configuration (배포 설정) +- **Domain**: `kis.tindevil.com` +- **Security Check**: + - `ALLOWED_HOSTS`: `["kis.tindevil.com", "localhost", "127.0.0.1"]` 추가 필수. + - **CORS**: 프론트엔드가 동일 도메인에서 서빙되므로 기본적으로 불필요하나, 로컬 개발/외부 연동 시 `ALLOW_ORIGINS` 설정 필요. + - **Cookie/Session**: `SameSite=Lax`, `Secure=True` (HTTPS proxy behind Dokploy/Traefik). + +--- + +## 2. 시스템 아키텍처 및 주요 모듈 + +### 2.1 Monolithic Architecture +- **Single Entry Point**: 사용자는 하나의 URL/포트로 접속. +- **Routing**: + - `/api/*`: REST API 요청 처리 + - `/ws/*`: WebSocket 요청 처리 + - `/*`: React SPA 정적 파일(index.html, js, css) 반환 + +### 2.2 Headless Execution Engine (매매 엔진) +- API 서버와 동일한 프로세스 내에서 백그라운드 스레드(Async Task)로 동작. +1. **Auto-Trading Scanner**: 매 1분마다 `auto_trade_robots` 설정을 스캔하여 로직(적립식, TS 등)에 따라 주문 생성. +2. **Order Monitoring Engine**: 실시간 시세를 수신하여 `reserved_orders`의 발동 조건을 감시하고 즉시 주문 집행. + +### 2.3 Data Infrastructure (데이터 인프라) +1. **Dynamic Real-time Subscription (Reference Counting)**: + - **Strategy**: 불필요한 트래픽 방지를 위해 **"필요할 때만 구독"**하는 레퍼런스 카운팅 방식을 사용. + - **Source 1 (Permanent)**: **보유 종목(Holdings)**은 앱 실행 시 무조건 구독 (`Ref +1`). + - **Source 2 (Transient)**: 프론트엔드에서 특정 종목 페이지 진입 시 구독 요청 (`Ref +1`), 이탈 시 해제 (`Ref -1`). + - **Decision**: `Reference Count > 0`일 때만 KIS WebSocket 연결 유지. 0이 되면 즉시 구독 해제. +2. **AI Analysis Proxy**: 프론트엔드의 AI 분석 요청을 수신하여 전용 API 터널을 통해 외부 AI 엔진과 통신 및 결과 JSON 파싱. + +### 2.4 Token Lifecycle Management (인증 토큰 관리) +- **Token Storage**: 발급된 Access Token과 만료 시각(`expiry`)을 메모리 및 DB에 저장. +- **Proactive Refresh**: API 호출 전 현재 시각이 만료 시각(통상 6~24시간)에 근접했는지 확인하여 선제적 갱신. +- **Reactive Refresh (Auto-Retry)**: API 호출 중 `401 Unauthorized` 또는 `EXPIRED_TOKEN` 오류 발생 시, + 1. 즉시 토큰 재발급을 수행. + 2. 실패했던 원래 요청을 새 토큰으로 **자동 재시도(Retry)**. + 3. 프론트엔드나 상위 로직에는 오류를 전파하지 않고 처리를 완료함. + +### 2.5 Rate Limiting & Throttling (속도 제한 및 조절) +- **Centralized Request Queue**: 모든 KIS API 호출은 단일 큐(`Blocking Queue`)를 통과해야 함. +- **Throttling Logic**: + - 각 호출 사이에는 설정된 간격(`kisApiDelayMs`, 기본 250ms)만큼 강제 대기(Delay) 수행. + - 연속된 주문이나 조회 요청이 발생해도 초당 4회 이하로 전송 속도를 물리적으로 제한하여 증권사 서버 차단을 방지. +- **Configurable Intervals**: + - `kisApiDelayMs`: API 호출 최소 간격 (기본 250ms) + - `newsScrapIntervalMin`: 뉴스 수집 주기 (기본 10분) + +### 2.6 Market Schedule Awareness (장 운영시간 인지) +백엔드는 **국내(Domestic)**와 **해외(Overseas)** 시장의 개장 시간을 독립적으로 관리하며, 장이 닫힌 동안에는 불필요한 API 호출과 자동매매 스캔을 중단(`Sleep Mode`)합니다. + +- **Check Logic**: 모든 자동화 로직은 실행 전 `IsMarketOpen(marketType)`을 확인해야 함. +- **Domestic (KST)**: 09:00 ~ 15:30 (평일, 공휴일 제외) +- **Overseas (US)**: 22:30 ~ 06:00 (썸머타임 적용 시 변동, 주말 제외) +- **Action**: + - **Open**: 실시간 시세 수신, 감시 주문 체크, 적립식 매수 실행. + - **Closed**: 단순 데이터 수집(뉴스) 외 트레이딩 로직 일시 정지. + +--- + +## 3. 상세 설계 명세서 + +백엔드 구현 시 아래 세 문서를 바이블로 삼아 개발을 진행하십시오. + +- 🏗️ **[DB 스키마 정의서](./tables.md)**: SQLite 테이블 구조 및 관계 정의. +- 📡 **[API 인터페이스 명세](./api.md)**: REST API 엔드포인트 및 WebSocket 토픽 정의. +- 📦 **[통합 데이터 모델](./models.md)**: API와 DB에서 공통으로 사용하는 JSON 구조 및 엔티티 정의. + +--- + +## 4. 운영 사이클 (Operational Cycle) + +| 작업명 | 주기 | 주요 내용 | +| :--- | :--- | :--- | +| **자동매매 스캔** | 1분 | 예약된 시각의 적립식 매수 및 전략 조건 확인 | +| **시세 감시** | 실시간 | 감시가 도달 시 즉시 주문 전송 | +| **관심종목 갱신** | Event | WebSocket 체결가 수신 시 즉시 DB 캐시 업데이트 | +| **마스터 동기화** | 일 1회 | 신규 상장/상폐 종목 리스트 업데이트 | +| **뉴스 수집** | 10분 | 실시간 금융 뉴스 스크랩 및 AI 분석 준비 | + +--- + +## 5. 시작 시퀀스 (Startup Sequence) + +백엔드 컨테이너 실행 시(`Initial Boot`), 시스템은 다음 순서대로 스스로 초기화를 수행하며 완전 자동화 모드로 진입합니다. + +### Phase 1: 시스템 초기화 +1. **DB 연결 및 마이그레이션**: SQLite 파일(`kis_stock.db`) 존재 여부 확인 및 테이블 생성. +2. **설정 로드**: `api_settings` 테이블에서 KIS AppKey, Secret, Telegram Token 등을 메모리에 로드. + +### Phase 2: 외부망 연결 (Connectivity) +3. **KIS API 인증**: + - 저장된 키로 **REST API Access Token** 발급 요청 (수명 24시간). + - (성공 시) **Base URL**로 실시간 **WebSocket Approval Key** 발급 요청 (수명 1회성/무제한). +4. **텔레그램 핸드쉐이크**: + - 봇 토큰이 유효한 경우, 관리자에게 _"System Online: BatchuKis Engine Started"_ 메시지 발송. + +### Phase 3: 데이터 동기화 (Data Sync) +5. **마스터 데이터 갱신**: + - `master_stocks` 테이블이 비어있거나, 마지막 갱신일이 어제인 경우 KIS로부터 전 종목 코드 다운로드. +6. **실시간 감시 복구**: + - `reserved_orders`에서 'MONITORING' 상태인 주문을 조회하여 실시간 시세 수신(WebSocket) 재구독. + +### Phase 4: 오토파일럿 가동 (Autopilot Engage) +7. **스케줄러 시작**: + - `Auto-Trading Scanner` (1분 주기) 가동. + - `Data Persistence Worker` (메모리 캐시 DB 저장) 가동. + - `News Scraper` (10분 주기) 가동. +8. **서버 준비 완료**: REST API(`:80`) 및 정적 파일 호스팅 시작. diff --git a/backend/TODO.md b/backend/TODO.md new file mode 100644 index 0000000..3b80fbc --- /dev/null +++ b/backend/TODO.md @@ -0,0 +1,90 @@ +# Backend Implementation Roadmap (TODO.md) + +이 문서는 `BatchuKis` 백엔드 개발을 위한 단계별 구현 체크리스트입니다. +사양서(`ReadMe.md`, `api.md`, `tables.md`, `models.md`)를 기반으로 작성되었습니다. + +--- + +## Phase 1: 프로젝트 스캐폴딩 (Scaffolding) +- [ ] **디렉토리 구조 생성** + - `app/core` (설정, 인증, 유틸) + - `app/db` (데이터베이스 연결, 모델) + - `app/api` (End-points) + - `app/services` (비즈니스 로직, 외부 통신) + - `app/workers` (백그라운드 스케줄러) +- [ ] **환경 설정 (`config.py`)** + - Pydantic `BaseSettings` 활용 + - `.env` 파일 연동 (APP_KEY, SECRET 등) + - Docker 환경 변수 처리 + - **Domain Config**: `ALLOWED_HOSTS=["kis.tindevil.com", ...]` 설정 추가 +- [ ] **FastAPI 기본 앱 작성 (`main.py`)** + - CORS 설정 + - Health Check 엔드포인트 (`/health`) + - Static Files Mount (Frontend `dist` 폴더 연결 준비) + +## Phase 2: 데이터베이스 구현 (Database) +- [ ] **SQLite 연동 (`database.py`)** + - SQLAlchemy `sessionmaker` 설정 (비동기 `AsyncSession` 권장) +- [ ] **ORM 모델 정의 (`db/models.py`)** + - `ApiSettings` (설정) + - `StockItem` (관심/보유 종목) + - `ReservedOrder` (감시 주문) + - `TradeOrder` (매매 기록) + - `CacheTable` (지수, 뉴스 등 임시 데이터) +- [ ] **마이그레이션 및 초기화** + - `init_db()` 함수 구현: 앱 시작 시 테이블 자동 생성 (`Base.metadata.create_all`) + +## Phase 3: 코어 인프라 (Core Infrastructure) +- [ ] **Rate Limiter (속도 제한 큐)** + - `RateLimiter` 클래스: `asyncio.Queue` 기반 중앙 제어 + - `TokenBucket` 또는 단순 `Delay` (250ms) 로직 구현 +- [ ] **Token Manager (인증 관리)** + - `KisAuth` 클래스: Access Token 발급/저장/갱신 + - **Auto-Retry**: 401 에러 인터셉터 및 재발급 로직 +- [ ] **Market Schedule (장 운영시간)** + - `MarketCalendar` 유틸: 현재 국내/해외 장 운영 여부 판단 (`IsMarketOpen`) + +## Phase 4: 내부 서비스 (Internal Services) +- [ ] **Brokerage Service (증권사 통신)** + - 파일: `app/services/kis_client.py` + - 구현: `api.md`의 **Section 9. Integration Map**에 정의된 API 엔드포인트 연동 + - 모든 호출은 `Rate Limiter` 경유 필수 +- [ ] **Realtime Manager (웹소켓)** + - `KisWebSocket` 클라이언트: Approval Key 발급 및 연결 + - **Reference Counting**: `Subscribe(code)`, `Unsubscribe(code)` 구현 + - 수신 데이터 파싱 및 Event Bus 전파 +- [ ] **AI Orchestrator** + - Gemini/OpenAI API 연동 핸들러 + +## Phase 5: API 엔드포인트 구현 (Endpoints) +- [ ] **Settings API** (`/api/settings`) + - API Key, Rate Limit 설정 조회/수정 +- [ ] **Stock/Market API** (`/api/kis/...`) + - 시세 조회, 차트 데이터, 관심종목 관리 +- [ ] **Trading API** (`/api/trade/...`) + - 주문 전송, 잔고 조회, 예약 주문(감시) CRUD +- [ ] **Real-time API** (Frontend WebSocket) + - `/ws/client`: 프론트엔드와 연결, KIS 데이터 중계 + +## Phase 6: 스케줄러 및 자동화 (Automation) +- [ ] **Startup Sequence** + - DB 체크 -> 토큰 로드 -> Telegram 알림 -> 스케줄러 시작 +- [ ] **Background Workers** + - `PersistenceWorker`: 메모리 내 시세 데이터 DB 주기적 저장 (Passive) + - `NewsScraper`: 네이버 뉴스 주기적 수집 + - `AutoTradingScanner`: 1분 단위 예약 주문 감시 + +## Phase 7: 통합 및 배포 (Integration) +- [ ] **Frontend 연동** + - React 빌드 (`npm run build`) -> backend/static 복사 + - 단일 포트(80) 서빙 테스트 +- [ ] **Docker 빌드** + - `docker-compose up` 테스트 + - Dokploy 배포 및 볼륨 마운트 확인 + +--- + +## 🎯 개발 규칙 (Rules) +1. **No Active Polling**: 시세는 반드시 웹소켓으로 수신 (관심/보유 종목 한정). +2. **Fail-Safe**: API 호출 실패 시 재시도 하되, 전체 장애로 번지지 않도록 예외 처리. +3. **Async First**: 모든 I/O(DB, Network)는 `async/await` 비동기 처리. diff --git a/backend/api.md b/backend/api.md index 7c0dacc..d54a6fa 100644 --- a/backend/api.md +++ b/backend/api.md @@ -1,91 +1,150 @@ # BatchuKis API Specification 이 문서는 프론트엔드와 백엔드 간의 통신을 위한 API 인터페이스 명세서입니다. -**데이터 모델(JSON 구조)의 상세 정의는 [models.md](./models.md)를 참조하십시오.** --- -## 1. 설정 및 시스템 (Settings) +## 1. 설정 및 시스템 (Settings & System) -### 1.1 전체 설정 가져오기 -- **URL**: `GET /api/settings` -- **Response**: `ApiSettings` (See models.md) +### 1.1 전체 설정 (Global Settings) +- **URL**: `GET /api/settings` / `POST /api/settings` +- **Description**: KIS API 키, 서비스 활성화 여부 등 전체 앱의 사용자 설정을 관리합니다. +- **Body (POST)**: `ApiSettings` object. -### 1.2 전체 설정 저장하기 -- **URL**: `POST /api/settings` -- **Body**: `ApiSettings` -- **Response**: `{ "success": boolean, "message": string }` - -### 1.3 AI 엔진 풀(Pool) 관리 -#### 1.3.1 등록된 AI 엔진 목록 조회 -- **URL**: `GET /api/settings/ai-configs` -- **Response**: `AiConfig[]` - -#### 1.3.2 신규 AI 엔진 추가 -- **URL**: `POST /api/settings/ai-configs` -- **Body**: `Omit` -- **Response**: `{ "id": string, "success": true }` +### 1.2 AI 엔진 관리 (AI Configurations) +- **URL**: `GET /api/settings/ai-configs` / `POST /api/settings/ai-configs` +- **Description**: 사용자 지정 AI 프로필(Gemini, Ollama 등) 목록을 관리합니다. +- **PUT/DELETE**: `PUT /api/settings/ai-configs/:id`, `DELETE /api/settings/ai-configs/:id` --- -## 2. 자산 및 잔고 (Portfolio) +## 2. 계좌 및 자산 (Account & Portfolio) -### 2.1 보유 종목 리스트 +### 2.1 계좌 요약 (Account Summary) +- **URL**: `GET /api/account/summary` +- **Response**: `{ "totalAssets": number, "buyingPower": number, "dailyProfit": number, "dailyProfitRate": number }` + +### 2.2 보유 자산 (Holdings) - **URL**: `GET /api/holdings` - **Query**: `?market=Domestic|Overseas` - **Response**: `HoldingItem[]` -### 2.2 계좌 요약 (자산/예수금) -- **URL**: `GET /api/account/summary` -- **Response**: `{ "totalAssets": number, "buyingPower": number }` +--- + +## 3. 관심종목 관리 (Watchlist) + +### 3.1 그룹 관리 (Watchlist Groups) +- **URL**: `GET /api/watchlists` / `POST /api/watchlists` +- **PUT/DELETE**: `PUT /api/watchlists/:id` (그룹명 수정 및 종목 코드 리스트 업데이트), `DELETE /api/watchlists/:id` +- **Response**: `WatchlistGroup[]` --- -## 3. 자동매매 전략 (Auto Trading) +## 4. 종목 및 시세 (Market Data) -### 3.1 로봇 리스트 조회 -- **URL**: `GET /api/auto-trades` -- **Response**: `AutoTradeConfig[]` +### 4.1 종목 검색 및 마스터 (Stock Discovery) +- **URL**: `GET /api/stocks/search?query=...` +- **URL**: `GET /api/kis/master-stocks?market=...` (로컬 검색성능을 위한 대량 데이터 수동 동기화용) -### 3.2 로봇 등록/수정 -- **URL**: `POST /api/auto-trades` -- **Body**: `AutoTradeConfig` (ID가 없으면 생성, 있으면 수정) -- **Response**: `{ "id": string }` +### 4.2 시세 및 차트 (Quotes & Charts) +- **URL**: `GET /api/kis/quotes/:code` (현재가 및 호가) +- **URL**: `GET /api/kis/ticks/:code?limit=...` (차트용 틱 데이터) + +### 4.3 발견 및 랭킹 (Market Discovery) +- **URL**: `GET /api/discovery/rankings?market=...&category=...` (거래량, 상승률 등) --- -## 4. 실시간 감시 주문 (Reserved Orders) +## 5. 뉴스 및 AI 분석 (News & AI Intelligence) -### 4.1 감시 목록 조회 -- **URL**: `GET /api/reserved-orders` -- **Response**: `ReservedOrder[]` +### 5.1 뉴스 스크랩 (News Feed) +- **URL**: `GET /api/news?query=...` +- **Description**: 네이버 API 등을 통해 실시간 금융 뉴스를 가져옵니다. -### 4.2 감시 등록 -- **URL**: `POST /api/reserved-orders` -- **Body**: `Omit` -- **Response**: `{ "id": string }` +### 5.2 AI 뉴스 분석 (AI News Briefing) +- **URL**: `POST /api/ai/analyze-news` +- **Body**: `{ "aiConfigId": string, "headlines": string[] }` +- **Response**: `{ "report": string, "sentimentMetadata": any[] }` + +### 5.3 AI 종목 분석 (AI Stock Deep-Dive) +- **URL**: `POST /api/ai/analyze-stock` +- **Body**: `{ "aiConfigId": string, "stockCode": string, "context": any }` --- -## 5. 종목 및 시세 (Market Data & Discovery) +## 6. 주문 및 거래 (Trading & History) -### 5.1 마스터 종목 리스트 (동기화용) -- **URL**: `GET /api/kis/master-stocks` -- **Query**: `?market=Domestic|Overseas` -- **Response**: `StockItem[]` +### 6.1 수동 주문 (Manual Trading) +- **URL**: `POST /api/trade/order` +- **Body**: `{ "code": string, "type": "BUY"|"SELL", "quantity": number, "price": number, "orderType": "MARKET"|"LIMIT" }` -### 5.2 개별 종목 시세 차트 데이터 -- **URL**: `GET /api/kis/ticks/:code` -- **Query**: `?limit=100` -- **Response**: `StockTick[]` +### 6.2 예약/감시 주문 (Reserved Orders) +- **URL**: `GET /api/reserved-orders` / `POST /api/reserved-orders` +- **DELETE**: `DELETE /api/reserved-orders/:id` -### 5.3 [NEW] 종목 발굴 랭킹 데이터 -- **URL**: `GET /api/discovery/rankings` -- **Query**: `?market=Domestic|Overseas&category=VOLUME|VALUE|GAIN|LOSS|FOREIGN_BUY|INSTITUTION_BUY` -- **Response**: `DiscoveryRankingResponse` (See models.md) -- **설명**: 토스증권 스타일의 발굴 페이지에 데이터를 공급합니다. 거래비율(buyRatio/sellRatio) 및 수급 데이터가 포함됩니다. +### 6.3 자동매매 전략 (Auto Trading) +- **URL**: `GET /api/auto-trades` / `POST /api/auto-trades` +- **Description**: AI 전략 로봇 등록 및 상태 관리. -### 5.4 [NEW] 실시간 커뮤니티 심리 요약 (AI 전용) -- **URL**: `GET /api/discovery/sentiment/:code` -- **Response**: `{ "insights": string[], "sentimentScore": number }` -- **설명**: 특정 종목에 대한 AI의 실시간 커뮤니티/뉴스 요약 정보를 반환합니다. +### 6.4 거래 기록 (Execution History) +- **URL**: `GET /api/history/orders` +- **Query**: `?page=...&limit=...&stockCode=...&startDate=...&endDate=...` +- **Response**: `TradeOrder[]` + +--- + +## 7. 실시간 데이터 (Real-time Data - WebSockets) + +백엔드는 KIS 실시간 시세 서버와 웹소켓을 유지하며, 프론트엔드에게 가공된 데이터를 스트리밍합니다. + +### 7.1 실시간 체결가 (Real-time Price) +- **Topic**: `/ws/market/price/:code` +- **Data**: 현재가, 등락률, 거래량 등 + +### 7.2 실시간 주문 알림 (Order Notifications) +- **Topic**: `/ws/trade/events` +- **Data**: 자신의 계좌에서 발생하는 체결 알림, 미체결 취소 알림 등 + +--- + +## 8. 내부 서비스 인터페이스 (Internal Engine Services) + +프론트엔드에 노출되지 않지만, 백엔드 엔진 내부 모듈 간의 협업 또는 외부 API 래핑을 위한 서비스 인터페이스입니다. + +### 8.1 BrokerageInterface (증권사 서버 통신) +- **`PlaceOrder(stockCode, side, qty, price, type)`**: KIS 서버로 최종 주문 패킷 전송. (Returns: `orderNo`) +- **`GetRealtimeApprovalKey()`**: 실시간 웹소켓 접속용 **Approval Key** 발급 및 갱신. +- **`FetchHoldings()`**: 계좌 내 전 종목 보유 및 평단가 수집. + +### 8.2 RealtimeManager (웹소켓 데이터 관리) +- **`AddSubscription(code, source)`**: 구독 요청 (`source`: 'HOLDING' | 'FRONTEND'). + - **Logic**: 해당 종목의 Reference Count 증가. `Count == 1`이 되는 순간 KIS 웹소켓 구독 전송. +- **`RemoveSubscription(code, source)`**: 구독 해제 요청. + - **Logic**: Reference Count 감소. `Count == 0`이 되면 KIS 웹소켓 구독 해제(자원 절약). +- **`OnStreamReceived(data)`**: 수신된 로우 데이터를 파싱하여 필요 모듈(`AutoTrader`, `FrontendWS`)로 브로드캐스팅. + +### 8.3 AiOrchestrator (AI 엔진 처리 핸들러) +- **`RequestAnalysis(aiConfigId, prompt)`**: 지정된 엔진 프로필을 사용하여 원격 AI API 호출. +- **`ApplySentiment(newsId, result)`**: 분석된 심리 결과를 DB(`news_cache`)에 업데이트 및 프론트 전송. + +### 8.4 Scheduler (배치 작업 제어) +- **`SyncMasterStocks()`**: 매일 새벽 종목 명칭 및 코드 리스트 동기화. +- **`PersistMarketDataCache()`**: 메모리에 수신된 실시간 현재가를 주기적으로 DB에 영구 저장 (API 호출 없음). +- **`RefreshNaverNews()`**: 10분 단위 키워드별 뉴스 스크랩 및 분석 트리거. + +--- + +## 9. KIS API Integration Map (증권사 연동 맵) + +`BrokerageInterface`가 실제 호출해야 할 한국투자증권(KIS) Open API 엔드포인트 매핑입니다. + +| Category | Internal Function | Domestic API (국내) | Overseas API (해외) | Note | +| :--- | :--- | :--- | :--- | :--- | +| **Auth** | `GetAuthToken` | `/oauth2/tokenP` | (Common) | OAuth2 Access Token | +| **Auth** | `GetWebsocketKey` | `/oauth2/approval` | (Common) | WebSocket Approval Key | +| **Order** | `PlaceOrder` | `/uapi/domestic-stock/v1/trading/order-cash` | `/uapi/overseas-stock/v1/trading/order` | 현금 매수/매도 | +| **Order** | `ModifyOrder` | `/uapi/domestic-stock/v1/trading/order-rvsecncl` | `/uapi/overseas-stock/v1/trading/order-rvsecncl` | 정정/취소 | +| **Price** | `GetCurrentPrice` | `/uapi/domestic-stock/v1/quotations/inquire-price` | `/uapi/overseas-stock/v1/quotations/price` | 현재가 조회 | +| **Price** | `GetDailyChart` | `/uapi/domestic-stock/v1/quotations/inquire-daily-price` | `/uapi/overseas-stock/v1/quotations/daily-price` | 일봉 데이터 | +| **Balance** | `FetchHoldings` | `/uapi/domestic-stock/v1/trading/inquire-balance` | `/uapi/overseas-stock/v1/trading/inquire-balance` | 잔고 조회 | +| **Socket** | `RealtimePrice` | `/tryit/H0STCNT0` (H0STCNT0) | `/tryit/HDFSCNT0` (HDFSCNT0) | 실시간 체결가 | diff --git a/backend/models.md b/backend/models.md index b53909f..e2e3ad8 100644 --- a/backend/models.md +++ b/backend/models.md @@ -23,26 +23,32 @@ ### 2.1 StockItem (종목 정보 - 확장됨) | 필드명 | 타입 | 필수 | 설명 | | :--- | :--- | :--- | :--- | -| code | string | Y | 종목 코드 (예: "005930", "NVDA") | +| code | string | Y | 종목 코드 | | name | string | Y | 종목 명칭 | | price | number | Y | 현재가 | | change | number | Y | 전일 대비 등락 금액 | | changePercent | number | Y | 전일 대비 등락률 (%) | | market | MarketType | Y | 소속 시장 | | volume | number | Y | 거래량 | -| tradingValue | number | N | 거래대금 (발굴 페이지용) | +| tradingValue | number | N | 거래대금 | | buyRatio | number | N | 실시간 매수 비율 (0~100) | | sellRatio | number | N | 실시간 매도 비율 (0~100) | | foreignNetBuy | number | N | 외국인 순매수량 | | institutionalNetBuy | number | N | 기관 순매수량 | -| per | number | N | 주가수익비율 | -| pbr | number | N | 주가순자산비율 | -| roe | number | N | 자기자본이익률 | +| per | number | N | PER | +| pbr | number | N | PBR | +| roe | number | N | ROE | | marketCap | number | N | 시가총액 | | dividendYield | number | N | 배당수익률 | | aiScoreBuy | number | Y | AI 매수 점수 (0~100) | | aiScoreSell | number | Y | AI 매도 점수 (0~100) | -| themes | string[] | N | 연관 테마 리스트 (JSON Array) | +| openPrice | number | N | 시가 | +| highPrice | number | N | 고가 | +| lowPrice | number | N | 저가 | +| themes | string[] | N | 연관 테마 | +| memo | string | N | 사용자 메모 | +| aiAnalysis | string | N | AI 분석 리포트 전문 | +| isHidden | boolean | N | 숨김 여부 | ### 2.2 ApiSettings (시스템 설정) | 필드명 | 타입 | 필수 | 설명 | @@ -56,6 +62,8 @@ | useNaverNews | boolean | Y | 네이버 뉴스 스크랩 여부 | | naverClientId | string | N | 네이버 Client ID | | naverClientSecret | string | N | 네이버 Client Secret | +| kisApiDelayMs | number | N | KIS API 호출 간격 (ms, 기본 250) | +| newsScrapIntervalMin | number | N | 뉴스 수집 주기 (분, 기본 10) | | aiConfigs | AiConfig[] | Y | 등록된 AI 엔진 목록 | | preferredNewsAiId | string | N | 뉴스 분석용 AI ID (FK) | | preferredStockAiId | string | N | 종목 분석용 AI ID (FK) | @@ -69,20 +77,20 @@ | id | string | Y | 고유 식별자 | | name | string | Y | 엔진 별칭 | | providerType | AiProviderType | Y | 제공자 유형 | -| modelName | string | Y | 모델 명칭 (예: "gemini-3-flash-preview") | -| baseUrl | string | N | API 엔드포인트 URL (Ollama 등) | +| modelName | string | Y | 모델 명칭 | +| baseUrl | string | N | API 엔드포인트 URL | ### 2.4 AutoTradeConfig (자동매매 로봇) | 필드명 | 타입 | 필수 | 설명 | | :--- | :--- | :--- | :--- | | id | string | Y | 식별자 | -| stockCode | string | N | 종목 코드 (개별 종목 대상인 경우) | +| stockCode | string | N | 종목 코드 | | stockName | string | Y | 대상 명칭 | -| groupId | string | N | 관심 그룹 ID (그룹 대상인 경우) | -| type | string | Y | "ACCUMULATION" (적립식) \| "TRAILING_STOP" | -| quantity | number | Y | 실행 시 주문 수량 | +| groupId | string | N | 관심 그룹 ID | +| type | string | Y | "ACCUMULATION" \| "TRAILING_STOP" | +| quantity | number | Y | 주문 수량 | | frequency | string | Y | "DAILY" \| "WEEKLY" \| "MONTHLY" | -| specificDay | number | N | 실행일 (0-6 요일 또는 1-31 날짜) | +| specificDay | number | N | 실행일 | | executionTime | string | Y | 실행 시각 ("HH:mm") | | trailingPercent | number | N | TS 사용 시 퍼센트 | | active | boolean | Y | 활성화 상태 | @@ -100,11 +108,53 @@ | triggerPrice | number | Y | 감시 기준 가격 | | trailingType | string | Y | "PERCENT" \| "AMOUNT" | | trailingValue | number | Y | 간격 값 | +| useStopLoss | boolean | N | 손절 기능 사용 여부 | +| stopLossType | string | N | "PERCENT" \| "AMOUNT" | +| stopLossValue | number | N | 손절 기준값 | +| sellAll | boolean | N | 전량 매도 여부 | +| highestPrice | number | N | 감시 시작 후 최고가(Trailing용) | +| lowestPrice | number | N | 감시 시작 후 최저가(Trailing용) | +| market | MarketType | Y | 마켓 구분 | | status | string | Y | "WAITING" \| "MONITORING" \| "TRIGGERED" \| "CANCELLED" | | createdAt | string | Y | 생성 일시 (ISO 8601) | | expiryDate | string | Y | 만료 일시 (ISO 8601) | -### 2.6 WatchlistGroup (관심 종목 그룹) +### 2.6 NewsItem (뉴스 정보) +| 필드명 | 타입 | 필수 | 설명 | +| :--- | :--- | :--- | :--- | +| title | string | Y | 뉴스 제목 | +| description | string | Y | 뉴스 요약 내용 | +| link | string | Y | 원문 링크 URL | +| pubDate | string | Y | 발행 일시 (ISO 8601) | +| sentiment | string | N | AI 분석 심리 ("POSITIVE" \| "NEGATIVE" \| "NEUTRAL") | +| relatedThemes | string[] | N | AI 추출 연관 테마 | +| relatedStocks | string[] | N | AI 추출 연관 종목명/코드 | + +### 2.7 TradeOrder (체결 기록 / 주문) +| 필드명 | 타입 | 필수 | 설명 | +| :--- | :--- | :--- | :--- | +| id | string | Y | 고유 식별자 | +| stockCode | string | Y | 종목 코드 | +| stockName | string | Y | 종목 명칭 | +| type | string | Y | "BUY" \| "SELL" | +| quantity | number | Y | 체결 수량 | +| price | number | Y | 체결 단가 | +| timestamp | string | Y | 체결 시각 (ISO 8601) | +| status | string | Y | "COMPLETED" \| "CANCELLED" \| "FAILED" | + +### 2.8 HoldingItem (보유 잔고 종목) +| 필드명 | 타입 | 필수 | 설명 | +| :--- | :--- | :--- | :--- | +| stockCode | string | Y | 종목 코드 | +| stockName | string | Y | 종목 명칭 | +| quantity | number | Y | 보유 수량 | +| avgPrice | number | Y | 평균 매입가 | +| currentPrice | number | Y | 현재가 | +| profit | number | Y | 평가 손익 | +| profitRate | number | Y | 수익률 (%) | +| marketValue | number | Y | 평가 금액 | + +### 2.9 WatchlistGroup (관심 종목 그룹) | 필드명 | 타입 | 필수 | 설명 | | :--- | :--- | :--- | :--- | | id | string | Y | 식별자 | @@ -112,11 +162,11 @@ | codes | string[] | Y | 포함된 종목 코드 배열 | | market | MarketType | Y | 마켓 구분 | -## 3. Discovery Models (종목 발굴용 전용 모델) +## 3. Discovery & AI Models (특수 목적 모델) ### 3.1 RankingCategory - **Type**: `string` -- **Values**: `"VOLUME"` (거래량), `"VALUE"` (거래대금), `"GAIN"` (상승), `"LOSS"` (하락), `"FOREIGN_BUY"` (외인매수), `"INSTITUTION_BUY"` (기관매수) +- **Values**: `"VOLUME"`, `"VALUE"`, `"GAIN"`, `"LOSS"`, `"FOREIGN_BUY"`, `"INSTITUTION_BUY"` ### 3.2 DiscoveryRankingResponse | 필드명 | 타입 | 필수 | 설명 | @@ -124,3 +174,14 @@ | category | RankingCategory | Y | 랭킹 카테고리 | | updatedAt | string | Y | 랭킹 갱신 시각 (ISO 8601) | | items | StockItem[] | Y | 순위별 종목 리스트 | + +### 3.3 AiAnalysisMetadata +- **Description**: 뉴스 리포트 분석 시 각 기사별로 매칭되는 메타데이터 구조. +```json +{ + "index": number, + "sentiment": "POSITIVE" | "NEGATIVE" | "NEUTRAL", + "themes": string[], + "stocks": string[] +} +``` diff --git a/backend/tables.md b/backend/tables.md index ea6c26c..72187e9 100644 --- a/backend/tables.md +++ b/backend/tables.md @@ -2,78 +2,107 @@ 이 문서는 데이터베이스 설계를 위한 테이블 명세서입니다. 모든 컬럼명은 [models.md](./models.md)의 필드명과 일치해야 합니다. -## 1. api_settings (사용자 및 API 설정) -- 단일 사용자 환경이므로 `id=1` 레코드만 사용. -- `aiConfigs`는 별도의 `ai_configs` 테이블과 Join하여 처리. +--- -## 2. ai_configs (AI 엔진 프로필) -- `id`: TEXT (PK) -- `name`: TEXT -- `providerType`: TEXT (gemini | openai-compatible) -- `modelName`: TEXT -- `baseUrl`: TEXT (Nullable) +## 1. System & Config (설정 제어) -## 3. holdings (현재 보유 종목) -- `code`: TEXT (PK) -- `name`: TEXT -- `avgPrice`: REAL -- `quantity`: INTEGER -- `market`: TEXT (Domestic | Overseas) +### 1.1 `api_settings` (전체 시스템 설정) +- **Primary Key**: `id` (INTEGER, ALWAYS 1) +- **Columns**: + - `appKey`, `appSecret`, `accountNumber`: TEXT + - `useTelegram`, `useNaverNews`: BOOLEAN + - `telegramToken`, `telegramChatId`: TEXT + - `naverClientId`, `naverClientSecret`: TEXT + - `kisApiDelayMs`, `newsScrapIntervalMin`: INTEGER + - `preferredNewsAiId`, `preferredStockAiId`, `preferredNewsJudgementAiId`, `preferredAutoBuyAiId`, `preferredAutoSellAiId`: TEXT (FK to `ai_configs.id`) -## 4. auto_trade_configs (자동매매 로봇) -- `id`: TEXT (PK) -- `stockCode`: TEXT (Nullable) -- `stockName`: TEXT -- `groupId`: TEXT (Nullable) -- `type`: TEXT (ACCUMULATION | TRAILING_STOP) -- `quantity`: INTEGER -- `frequency`: TEXT (DAILY | WEEKLY | MONTHLY) -- `specificDay`: INTEGER -- `executionTime`: TEXT -- `trailingPercent`: REAL -- `active`: BOOLEAN -- `market`: TEXT +### 1.2 `ai_configs` (AI 엔진 프로필 저장소) +- **Primary Key**: `id` (TEXT) +- **Columns**: `name`, `providerType`, `modelName`, `baseUrl` (TEXT) -## 5. reserved_orders (실시간 감시/예약 주문) -- `id`: TEXT (PK) -- `stockCode`: TEXT -- `stockName`: TEXT -- `type`: TEXT (BUY | SELL) -- `quantity`: INTEGER -- `monitoringType`: TEXT -- `triggerPrice`: REAL -- `trailingType`: TEXT -- `trailingValue`: REAL -- `status`: TEXT -- `createdAt`: DATETIME -- `expiryDate`: DATETIME +--- -## 6. watchlist_groups (관심 종목 그룹) -- `id`: TEXT (PK) -- `name`: TEXT -- `codes`: TEXT (JSON String Array - e.g. '["005930", "NVDA"]') -- `market`: TEXT +## 2. Account & Portfolio (자산 데이터) -## 7. [NEW] discovery_rank_cache (발굴 랭킹 캐시) -- `category`: TEXT (PK - VOLUME, VALUE 등) -- `market`: TEXT (PK) -- `rank_json`: TEXT (JSON String - StockItem 리스트 보관) -- `updated_at`: DATETIME -- **용도**: 랭킹 연산은 리소스가 많이 들므로 1~5분 단위로 배치 처리 후 캐시된 데이터를 API로 제공. +### 2.1 `account_status` (계좌 요약 정보) +- **Primary Key**: `id` (INTEGER, ALWAYS 1) +- **Columns**: `totalAssets`, `buyingPower`, `dailyProfit`, `dailyProfitRate` (REAL) +- **Update Frequency**: API 동기화 마다 갱신. -## 8. [NEW] stock_stats (종목별 확장 통계) -- `code`: TEXT (PK) -- `tradingValue`: REAL -- `buyRatio`: INTEGER -- `sellRatio`: INTEGER -- `foreignNetBuy`: INTEGER -- `institutionalNetBuy`: INTEGER -- **용도**: `StockItem` 테이블을 직접 확장하거나 별도 통계 테이블로 관리하여 발굴 데이터 조회 성능 최적화. -## 9. market_index_history (시장 지수 이력) -- `index_id`: TEXT (PK - KOSPI, KOSDAQ, NASDAQ, SP500, USDKRW 등) -- `timestamp`: DATETIME (PK - 1시간 단위 정규화된 시각) -- `value`: REAL -- `change`: REAL -- `change_percent`: REAL -- `updated_at`: DATETIME (실제 마지막 갱신 시각) -- **용도**: 트렌드 분석 및 대시보드 인덱스 카드 표시용. 백엔드가 5분 단위로 조회하되, DB에는 1시간 단위로 마지막 데이터를 Upsert 하여 누적. +### 2.2 `holdings` (보유 자산 목록) +- **Primary Key**: `stockCode` (TEXT) +- **Columns**: `stockName` (TEXT), `quantity` (INTEGER), `avgPrice`, `currentPrice`, `profit`, `profitRate`, `marketValue` (REAL) + +--- + +## 3. Market & Discovery (시세 및 탐색 데이터) + +### 3.1 `master_stocks` (전체 종목 마스터) +- **Primary Key**: `code` (TEXT) +- **Columns**: + - `name`, `market` (TEXT) + - `per`, `pbr`, `roe`, `marketCap`, `dividendYield` (REAL) + - `memo` (TEXT), `isHidden` (BOOLEAN) +- **용도**: 종목 검색, 메타데이터 제공 및 사용자 설정(메모/숨김) 저장. + +### 3.2 `news_cache` (뉴스 및 분석 캐시) +- **Primary Key**: `news_id` (TEXT - URL 해시 등) +- **Columns**: + - `title`, `description`, `link`, `pubDate`: TEXT + - `sentiment`: TEXT + - `relatedThemes`, `relatedStocks`: TEXT (JSON Array) +- **용도**: 동일 뉴스에 대한 중복 AI 분석 방지 및 히스토리 제공. + +### 3.3 `discovery_ranking_cache` (발굴 랭킹 데이터) +- **Primary Key**: `category` + `market` (Composite) +- **Columns**: `updated_at` (DATETIME), `items_json` (TEXT - `StockItem[]` JSON) + +### 3.4 `stock_stats` (종목별 확장 통계) +- **Primary Key**: `code` (TEXT) +- **Columns**: + - `tradingValue` (REAL) + - `buyRatio`, `sellRatio`, `foreignNetBuy`, `institutionalNetBuy` (INTEGER) + - `aiScoreBuy`, `aiScoreSell` (INTEGER) +- **용도**: `StockItem` 확장을 위한 분석/수급 데이터 저장. + +--- + +## 4. Watchlist (보관함) + +### 4.1 `watchlist_groups` (그룹 정의) +- **Primary Key**: `id` (TEXT) +- **Columns**: `name` (TEXT), `market` (TEXT) + +### 4.2 `watchlist_items` (그룹별 포함 종목) +- **Primary Key**: `group_id` + `stock_code` (Composite) +- **Columns**: `added_at` (DATETIME) +- **Relationship**: `group_id` -> `watchlist_groups.id` + +--- + +## 5. Trading & Automation (거래 및 자동화) + +### 5.1 `trade_history` (전체 체결 내역) +- **Primary Key**: `id` (TEXT) +- **Columns**: + - `stockCode`, `stockName`, `type` (TEXT) + - `quantity` (INTEGER), `price` (REAL) + - `timestamp` (DATETIME), `status` (TEXT) + +### 5.2 `auto_trade_robots` (자동매매 전략 로봇) +- **Primary Key**: `id` (TEXT) +- **Columns**: + - `stockCode`, `stockName`, `groupId`, `type`, `frequency`, `executionTime`, `market`: TEXT + - `quantity`, `specificDay`: INTEGER + - `trailingPercent`: REAL + - `active`: BOOLEAN + +### 5.3 `reserved_orders` (실시간 감시 주문) +- **Primary Key**: `id` (TEXT) +- **Columns**: + - `stockCode`, `stockName`, `type`, `monitoringType`, `trailingType`, `status`, `market`: TEXT + - `quantity`: INTEGER + - `triggerPrice`, `trailingValue`, `stopLossValue`, `highestPrice`, `lowestPrice`: REAL + - `useStopLoss`, `sellAll`: BOOLEAN + - `stopLossType`: TEXT + - `createdAt`, `expiryDate`: DATETIME diff --git a/pages/Settings.tsx b/pages/Settings.tsx index f503347..96de9d3 100644 --- a/pages/Settings.tsx +++ b/pages/Settings.tsx @@ -78,6 +78,7 @@ const Settings: React.FC = ({ settings, onSave }) => {
setFormData({...formData, accountNumber: v})} placeholder="예: 50061234-01" />
+ setFormData({...formData, kisApiDelayMs: parseInt(v) || 250})} type="number" placeholder="250" /> @@ -226,6 +227,9 @@ const Settings: React.FC = ({ settings, onSave }) => {
setFormData({...formData, naverClientId: v})} placeholder="Naver Client ID" /> setFormData({...formData, naverClientSecret: v})} type="password" placeholder="Naver Client Secret" /> +
+ setFormData({...formData, newsScrapIntervalMin: parseInt(v) || 10})} type="number" placeholder="10" /> +
diff --git a/types.ts b/types.ts index f059aa1..c8917eb 100644 --- a/types.ts +++ b/types.ts @@ -29,6 +29,8 @@ export interface ApiSettings { useNaverNews: boolean; naverClientId: string; naverClientSecret: string; + kisApiDelayMs?: number; + newsScrapIntervalMin?: number; aiConfigs: AiConfig[]; preferredNewsAiId?: string; preferredStockAiId?: string;