Files
web-page-backend/stock-lab/API_SPEC.md
gahusb a826e00399 feat(stock): NXT 시간외 거래가를 정규장 마감 후 자동 연결
네이버 모바일 주식 API의 overMarketPriceInfo를 인식해 NXT 프리/애프터마켓
운영 중이면 overPrice를 current_price로 자동 전환. 포트폴리오 응답에
price_session(REGULAR/NXT_PRE/NXT_AFTER/CLOSED)과 price_as_of 메타 동봉.

이전엔 closePrice만 사용해 15:30 이후 NXT 거래가 진행 중이어도 평가금액이
동결됐음. 이제 가격이 자연스럽게 이어짐. _select_price_from_response는
순수 함수로 분리, unittest 8케이스로 회귀 방지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:32:10 +09:00

241 lines
5.5 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,
"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
}
}
```
> **주의**: 현재가 조회에 실패한 종목은 `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
```