Files
web-page-backend/stock/API_SPEC.md
gahusb 5da7a0040b fix(stock,docs): portfolio total_buy 수량 곱산 + insta-trends spec 변경 이력 (F4 + F6)
[F4] /api/portfolio 응답의 summary.total_buy가 종목별 단가 × 수량의 합이
되도록 fix. 기존 인라인 코드가 purchase_price를 수량 미곱산으로 단순
누적해 명세(qty 100 · avg 72000 → 7,200,000)와 어긋났음. API_SPEC.md에
purchase_price 필드 의미 + total_buy 계산식 명시. test 3건 (단가 곱산,
avg_price 폴백, 다종목 합산).

[F6] insta-trends spec/plan 상단에 "google_trends → youtube_trending"
변경 이력 추가. Google Trends endpoint 폐기로 source 교체된 이력이
본문 검색 시 혼란 주는 문제 차단. 사유 cross-ref:
feedback_external_data_sources.md
2026-05-17 14:06:19 +09:00

246 lines
5.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📈 Stock Lab API Specification
프론트엔드 연동을 위한 주식 서비스 API 명세서입니다.
**Base URL**: `/api`
---
## 1. 💰 계좌 잔고 조회
현재 연결된 한국투자증권 계좌의 잔고와 보유 종목을 조회합니다.
- **URL**: `/trade/balance`
- **Method**: `GET`
- **Description**: Windows AI Server를 통해 실시간 잔고를 가져옵니다.
### Response Example
```json
{
"holdings": [
{
"code": "005930",
"name": "삼성전자",
"qty": 10,
"buy_price": 72000.0,
"current_price": 74500.0,
"profit_rate": 3.47
}
],
"summary": {
"total_eval": 15400000,
"deposit": 5000000,
"note": "정상 조회됨"
}
}
```
---
## 2. 🤖 AI 자동 매매 (분석/주문)
AI에게 현재 잔고와 뉴스를 기반으로 매매 판단을 요청합니다.
- **URL**: `/trade/auto`
- **Method**: `POST`
- **Description**: 분석에는 수 초~수십 초가 소요될 수 있습니다. (타임아웃 주의)
### Response Example (성공 - JSON 파싱 완료)
```json
{
"status": "success",
"decision": {
"action": "BUY",
"ticker": "000660",
"quantity": 10,
"reason": "반도체 업황 개선 뉴스 다수 포착 및 현금 비중 과다"
},
"trade_result": {
"success": true,
"order_no": "1234567"
}
}
```
### Response Example (실패 - AI가 JSON을 안 줬을 때)
AI가 말로 설명하느라 JSON 포맷을 어긴 경우입니다. `raw_response`를 화면에 그대로 보여주는 것을 권장합니다.
```json
{
"status": "failed_parse",
"raw_response": "잔고 현황을 분석해 보겠습니다...\n결정:\n```\n{\n ... \n}\n```"
}
```
**Frontend 처리 권장사항**: `status``failed_parse`라면 `raw_response` 텍스트를 `pre` 태그 등으로 그대로 노출하거나, 정규식으로 JSON 부분만 추출하여 보여주세요.
---
## 3. 📰 뉴스 조회
DB에 저장된 최신 뉴스를 조회합니다.
- **URL**: `/stock/news`
- **Method**: `GET`
- **Params**:
- `limit`: 개수 (기본 20)
- `category`: `domestic` (국내) | `overseas` (해외)
### Response Example
```json
[
{
"id": 105,
"title": "삼성전자, 3분기 영업익 2.4조... 전년비 77% 감소",
"link": "https://n.news.naver.com/...",
"published_at": "2024-09-25T09:00:00",
"sentiment": "negative"
}
]
```
---
## 4. 📊 지수 조회
KOSPI, KOSDAQ 등 주요 지수를 조회합니다.
- **URL**: `/stock/indices`
- **Method**: `GET`
### Response Example
```json
{
"KOSPI": {
"value": "2450.55",
"change": "-10.23",
"percent": "-0.42%"
},
"USD/KRW": {
"value": "1340.50",
"change": "5.00",
"percent": "0.37%"
}
}
```
---
## 5. 📂 포트폴리오 (수동 입력)
KB증권·삼성증권 등 Open API 미제공 증권사용.
보유 종목을 수동 등록하면 **현재가는 네이버 금융에서 자동 조회** (3분 캐시)하여 손익을 계산해 반환합니다.
---
### 5-1. 전체 조회
- **URL**: `GET /portfolio`
- **Description**: 등록된 모든 종목의 현재가·평가금액·손익을 포함하여 반환합니다.
#### Response
```json
{
"holdings": [
{
"id": 1,
"broker": "KB증권",
"ticker": "005930",
"name": "삼성전자",
"quantity": 100,
"avg_price": 72000,
"purchase_price": 72000,
"current_price": 74500,
"price_session": "NXT_AFTER",
"price_as_of": "2026-05-11T19:21:40+09:00",
"eval_amount": 7450000,
"profit_amount": 250000,
"profit_rate": 3.47
}
],
"summary": {
"total_buy": 7200000,
"total_eval": 7450000,
"total_profit": 250000,
"total_profit_rate": 3.47
}
}
```
> **`purchase_price` 필드**: 종목별 매입 단가(1주당). 사용자가 수동 등록한 매입가가
> 평균단가(`avg_price`)와 다를 때 표시용으로 분리한다. 미설정 시 `avg_price`로 폴백.
> `summary.total_buy = SUM(purchase_price × quantity)` (CODE_REVIEW F4에서 명세 정합화).
> **주의**: 현재가 조회에 실패한 종목은 `current_price`, `eval_amount`, `profit_amount`, `profit_rate` 가 `null`로 반환됩니다.
> 프론트에서 `null` 체크 후 `"조회 실패"` 등으로 표시해 주세요.
> **현재가 출처(`price_session`)**: 정규장 마감 후 NXT 시간외 거래가 진행 중이면 NXT 가격으로 자동 전환됩니다.
> - `REGULAR` — KRX 정규장 진행중(09:0015:30) 실시간 가격
> - `NXT_PRE` — NXT 프리마켓(08:0008:50) 거래가
> - `NXT_AFTER` — NXT 애프터마켓(15:3020:00) 거래가
> - `CLOSED` — 모든 세션 마감, 정규장 종가 노출
>
> `price_as_of`는 가격이 마지막으로 형성된 시각(ISO 8601, KST). HTML 폴백 경로에서는 `null`일 수 있음.
---
### 5-2. 종목 추가
- **URL**: `POST /portfolio`
- **Status**: `201 Created`
#### Request Body
```json
{
"broker": "KB증권",
"ticker": "005930",
"name": "삼성전자",
"quantity": 100,
"avg_price": 72000
}
```
| 필드 | 타입 | 설명 |
|------|------|------|
| `broker` | string | 증권사명 (자유 입력) |
| `ticker` | string | 종목 코드 6자리 |
| `name` | string | 종목명 |
| `quantity` | integer | 보유 수량 |
| `avg_price` | integer | 평균 매입가 (원) |
#### Response
```json
{ "id": 1, "ok": true }
```
---
### 5-3. 종목 수정
- **URL**: `PUT /portfolio/{id}`
- **Description**: 변경할 필드만 포함하면 됩니다 (부분 수정).
#### Request Body (모든 필드 Optional)
```json
{ "quantity": 150 }
```
#### Response
```json
{ "ok": true }
```
#### Error (존재하지 않는 id)
```json
{ "error": "Item not found" } // HTTP 404
```
---
### 5-4. 종목 삭제
- **URL**: `DELETE /portfolio/{id}`
#### Response
```json
{ "ok": true }
```
#### Error (존재하지 않는 id)
```json
{ "error": "Item not found" } // HTTP 404
```