Files
goldenChart/docs/모의투자_투자금_관리_설계.md
2026-05-31 03:47:07 +09:00

467 lines
20 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.
# 모의투자 투자금·종목별 관리 설계
상용 주식/HTS 프로그램(키움 영웅문 모의, NH/미래 HTS 모의, TradingView Paper 등)에서 통상 제공하는 **계좌·투자금·종목별 배분·수익률·거래내역** 구조를 기준으로, goldenChart **현재 구현(as-is)****목표(to-be)** 를 정리한 설계서입니다.
---
## 1. 목표
| 목표 | 설명 |
|------|------|
| **투자금 단일 원천** | 모의 계좌의 현금·총자산·초기자본·실현/평가 손익을 DB에서 일관되게 관리 |
| **종목별 투자 한도** | 종목마다 최대 투자금(또는 비율)을 두고 매수 시 한도 초과 방지 |
| **투자금 설정 vs 관리 분리** | 설정(규칙·초기화) 화면과 운영(잔고·보유·체결) 화면 역할 분리 |
| **종목별 매수/매도** | 수동 주문·전략 자동매매가 동일 계좌·동일 한도 규칙을 따름 |
| **가상매매 연동** | 가상투자 대시보드·모의투자 메뉴가 **동일 `gc_paper_*` 계좌** 를 참조 |
---
## 2. 현재 구현 (As-Is)
### 2.1 DB (V18)
```
gc_app_settings ← 전역: 초기자본, 수수료, 슬리피지, 자동매매 ON/OFF, 매수 예산 %
gc_paper_account ← device_id당 1계좌: cash_balance, realized_pnl
├── gc_paper_position ← 종목별: quantity, avg_price (한도·배분 없음)
└── gc_paper_trade ← 체결 이력: BUY/SELL, fee, cash_after
```
### 2.2 API
| 메서드 | 경로 | 용도 |
|--------|------|------|
| GET/POST | `/api/paper/summary` | 계좌 요약·보유·평가손익(markPrices 선택) |
| GET | `/api/paper/trades` | 체결 목록 |
| POST | `/api/paper/orders` | 즉시 체결 주문 |
| POST | `/api/paper/reset` | 계좌 초기화 |
### 2.3 화면
| 화면 | 위치 | 역할 |
|------|------|------|
| **가상매매 설정** | 설정 → 가상매매 | 초기자본·수수료·자동매매 (전역) |
| **모의투자 설정 탭** | 모의투자 좌측 | 위와 동일 항목 + 계좌 초기화 |
| **모의투자 대시보드** | 메뉴 모의투자 | KPI·보유·체결·차트·수동 매수/매도 |
| **가상투자** | 메뉴 가상투자 | 전략 모니터링 + `placePaperOrder` 자동/수동 |
### 2.4 부족한 점 (Gap)
- **종목별 투자금 한도** 없음 → 전역 `paper_auto_trade_budget_pct` 만 존재
- **현금 변동 원장**(입금/출금/수수료/초기화) 미분리 → `cash_after` 만 체결에 기록
- **일별 자산 스냅샷** 없음 → 수익률 차트는 프론트가 체결만 재구성
- **미체결 주문** 없음 → 모두 즉시 체결
- **종목별 실현손익·회전율** 집계 테이블 없음
- **투자금 설정** 이 설정 앱 + 모의투자에 이중 노출
---
## 3. 상용 모의투자 벤치마크
| 영역 | 통상 기능 | goldenChart 매핑 |
|------|-----------|------------------|
| 계좌 개설 | 초기 예탁금 설정 | `paper_initial_capital` + `gc_paper_account` |
| 계좌 초기화 | 잔고·보유·내역 리셋 | `POST /paper/reset` (유지·이력 테이블 추가 권장) |
| 예수금/주문가능 | 현금, D+0 가용 | `cash_balance` `reserved_cash` |
| 종목별 매수한도 | 종목당 상한 또는 비중 상한 | **신규** `gc_paper_symbol_allocation` |
| 보유종목 | 수량·평단·평가·손익률·비중 | `gc_paper_position` + summary API 확장 |
| 체결내역 | 일자·종목·매수/매도·가격·수량·수수료 | `gc_paper_trade` (필터·페이징 추가) |
| 자산추이 | 일별 총자산 그래프 | **신규** `gc_paper_daily_snapshot` |
| 원장/자금변동 | 입출금·수수료·배당·이자 | **신규** `gc_paper_cash_ledger` |
| 수동 주문 | 지정가/시장가·수량·금액 | `TradeOrderPanel` + `/paper/orders` |
| 자동매매 | 전략 시그널 연동 | STOMP 시그널 → `placePaperOrder` |
---
## 4. DB 설계 (To-Be)
### 4.1 ER 개요
```mermaid
erDiagram
gc_app_settings ||--o{ gc_paper_account : "defaults"
gc_paper_account ||--o{ gc_paper_position : holds
gc_paper_account ||--o{ gc_paper_trade : fills
gc_paper_account ||--o{ gc_paper_symbol_allocation : limits
gc_paper_account ||--o{ gc_paper_cash_ledger : cash_flow
gc_paper_account ||--o{ gc_paper_daily_snapshot : eod
gc_paper_account ||--o{ gc_paper_reset_log : history
gc_paper_symbol_allocation }o--|| gc_strategy : optional
```
### 4.2 테이블 상세
#### (기존 확장) `gc_paper_account`
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `user_id` / `device_id` | | 기존 |
| `cash_balance` | DECIMAL(20,2) | 예수금 |
| `reserved_cash` | DECIMAL(20,2) **NEW** | 미체결·예약 금액 합 (Phase 2) |
| `realized_pnl` | DECIMAL(20,2) | 누적 실현손익 |
| `initial_capital_snapshot` | DECIMAL(20,2) **NEW** | 마지막 초기화 시점 기준 초기자본 |
| `reset_count` | INT **NEW** | 초기화 횟수 |
| `last_reset_at` | DATETIME **NEW** | |
| `status` | VARCHAR(20) **NEW** | `ACTIVE` \| `FROZEN` |
| `created_at` / `updated_at` | | |
**파생 지표(API 계산, 컬럼 비저장 권장)**
- `orderable_cash` = `cash_balance` `reserved_cash`
- `stock_eval_amount` = Σ(보유 수량 × markPrice)
- `total_asset` = `cash_balance` + `stock_eval_amount`
- `unrealized_pnl` = `stock_eval_amount` Σ(수량 × 평단)
- `total_return_pct` = (`total_asset` `initial_capital_snapshot`) / `initial_capital_snapshot` × 100
#### (신규) `gc_paper_symbol_allocation` — 종목별 투자금 설정
상용 HTS의 **종목별 매수 한도 / 관심종목 투자비중** 에 해당.
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `account_id` | BIGINT FK | |
| `symbol` | VARCHAR(50) | KRW-BTC 등 |
| `korean_name` | VARCHAR(100) | |
| `max_invest_krw` | DECIMAL(20,2) | 종목 최대 투자금(상한) |
| `budget_pct` | DECIMAL(8,4) NULL | 총자산 대비 % (max와 택1 또는 min 적용) |
| `strategy_id` | BIGINT NULL | 연결 전략(가상투자 카드와 동기) |
| `is_active` | TINYINT(1) | 0이면 신규 매수 차단 |
| `pinned` | TINYINT(1) | 관심 고정 |
| `sort_order` | INT | UI 정렬 |
| `created_at` / `updated_at` | | |
**UK**: `(account_id, symbol)`
**파생(체결·보유 기준 계산)**
| 필드 | 계산 |
|------|------|
| `invested_krw` | 보유 평가금 또는 `quantity × avg_price` |
| `available_buy_krw` | `max_invest_krw` `invested_krw` (음수면 0) |
| `symbol_return_pct` | (평가 원가) / 원가 × 100 |
| `weight_pct` | `eval_amount` / `total_asset` × 100 |
#### (기존 유지) `gc_paper_position`
| 컬럼 | 비고 |
|------|------|
| `quantity`, `avg_price` | 평단가 가중평균 유지 (기존 로직) |
| (선택) `first_buy_at` | 최초 매수 시각 — 통계용 |
#### (기존 확장) `gc_paper_trade`
| 컬럼 | 타입 | 설명 |
|------|------|------|
| 기존 컬럼 | | |
| `realized_pnl_delta` | DECIMAL(20,2) NULL **NEW** | 매도 시 해당 체결 실현손익 |
| `position_qty_after` | DECIMAL(30,12) NULL **NEW** | 체결 후 잔량 |
| `note` | VARCHAR(200) NULL **NEW** | 메모 |
**인덱스 추가**: `(account_id, symbol, created_at)`, `(account_id, created_at DESC)`
#### (신규) `gc_paper_cash_ledger` — 자금 변동 원장
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `account_id` | BIGINT FK | |
| `entry_type` | VARCHAR(30) | 아래 enum |
| `amount` | DECIMAL(20,2) | +입금 / −출금 |
| `cash_before` | DECIMAL(20,2) | |
| `cash_after` | DECIMAL(20,2) | |
| `ref_trade_id` | BIGINT NULL | 체결 연결 |
| `symbol` | VARCHAR(50) NULL | |
| `created_at` | DATETIME | |
**`entry_type` enum**
- `INITIAL` — 계좌 생성·초기화 시 초기 예탁
- `BUY_SETTLE` — 매수 결제
- `SELL_SETTLE` — 매도 입금
- `FEE` — 수수료 (체결에 포함 시 생략 가능)
- `ADJUST` — 관리자/시스템 조정
- `RESET` — 초기화 전 스냅샷 역산용
#### (신규) `gc_paper_daily_snapshot` — 일별 자산 (수익률 차트)
| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `account_id` | BIGINT FK | |
| `snapshot_date` | DATE | KST 기준 |
| `cash_balance` | DECIMAL(20,2) | |
| `stock_eval_amount` | DECIMAL(20,2) | EOD 시세 기준 |
| `total_asset` | DECIMAL(20,2) | |
| `daily_pnl` | DECIMAL(20,2) | 전일 대비 |
| `daily_return_pct` | DECIMAL(10,4) | |
| `cumulative_return_pct` | DECIMAL(10,4) | 초기자본 대비 |
**UK**: `(account_id, snapshot_date)` — 스케줄러: 매일 00:05 KST 또는 마지막 틱 후
#### (신규) `gc_paper_reset_log` — 초기화 이력
| 컬럼 | 설명 |
|------|------|
| `account_id`, `reset_at`, `initial_capital`, `cash_before`, `positions_json`, `reason` | 감사·복구 |
#### (Phase 2, 선택) `gc_paper_order` — 미체결 주문
지정가·예약 매수 등 확장 시. 현재는 **즉시 체결** 유지하고 Phase 2로 미룸.
### 4.3 전역 설정 (`gc_app_settings` — 기존 유지)
| 컬럼 | 용도 |
|------|------|
| `paper_trading_enabled` | 모의투자 마스터 스위치 |
| `paper_initial_capital` | 초기화·신규 계좌 기본 예탁금 |
| `paper_fee_rate_pct` / `paper_slippage_pct` / `paper_min_order_krw` | |
| `paper_auto_trade_enabled` / `paper_auto_trade_budget_pct` | **전역** 자동매수 시 가용현금 % |
**규칙 우선순위 (매수 가능 금액)**
```
min(
orderable_cash,
orderable_cash × paper_auto_trade_budget_pct / 100, -- 자동매매 시
symbol.available_buy_krw -- 종목 한도
)
```
### 4.4 마이그레이션 제안
| 파일 | 내용 |
|------|------|
| `V51__paper_investment_management.sql` | account 확장, symbol_allocation, cash_ledger, daily_snapshot, reset_log, trade 인덱스 |
| 데이터 백필 | 기존 account → `initial_capital_snapshot` = app.initial_capital; allocation은 보유 종목만 max=초기자본으로 seed |
---
## 5. 화면 설계
### 5.1 메뉴·정보 구조
```
설정
└── [모의투자 설정] ← 투자금·규칙·초기화 (관리자/최초 설정 성격)
모의투자 (Paper Trading) ← 투자금 관리·운영 허브
├── [투자금 관리] (기본) ← 계좌 요약·보유·종목별 수익
├── [거래/주문] ← 매수·매도 패널 (기존 우측)
├── [거래내역] ← 체결·필터
└── [성과/분석] ← MDD·샤프·자산추이 (기존 차트 강화)
가상투자 (Virtual) ← 전략 모니터 (체결은 동일 paper API)
```
**역할 분리 원칙**
| 화면 | 사용자 질문 |
|------|-------------|
| **투자금 설정** | “얼마로 시작하고, 수수료·자동매매·종목당 한도는?” |
| **투자금 관리** | “지금 얼마 벌었고, 종목별로 얼마 묶여 있나?” |
| **거래/주문** | “이 종목 사고/팔려면?” |
### 5.2 투자금 설정 화면 (`PaperInvestmentSettingsPage` 또는 설정 탭 통합)
**레이아웃 (1열 폼 + 위험 작업 하단)**
```
┌─────────────────────────────────────────┐
│ 모의투자 설정 │
├─────────────────────────────────────────┤
│ [ON] 모의투자 사용 │
│ 초기 자본 (KRW) [ 10,000,000 ] │
│ 수수료 (%) [ 0.05 ] │
│ 슬리피지 (%) [ 0.00 ] │
│ 최소 주문 (KRW) [ 5,000 ] │
├─────────────────────────────────────────┤
│ 자동매매 │
│ [ON] 전략 시그널 자동 체결 │
│ 매수 예산 (가용현금 %) [ 95 ] │
├─────────────────────────────────────────┤
│ 기본 종목 한도 (신규 종목 추가 시) │
│ ○ 총자산의 [ 20 ] % ○ 고정 [ 2,000,000]│
├─────────────────────────────────────────┤
│ [ 계좌 초기화 ] (confirm + reset_log) │
└─────────────────────────────────────────┘
```
- **저장**: `gc_app_settings` + (선택) 계좌 `initial_capital_snapshot` 동기
- **초기화**: `POST /paper/reset` + `gc_paper_reset_log` + ledger `RESET`
### 5.3 투자금 관리 화면 (`PaperInvestmentManage` — 모의투자 메인 상단/탭)
**상용 HTS “계좌평가 / 잔고” 패널에 해당**
```
┌──────────────────────────────────────────────────────────────┐
│ 총자산 ₩12,345,678 │ 수익률 +12.3% │ 평가손익 +234,567 │
│ 예수금 ₩3,000,000 │ 실현손익 +50,000 │ 주문가능 ₩2,850,000 │
└──────────────────────────────────────────────────────────────┘
┌─ 보유 종목 (종목별 투자금·수익률) ─────────────────────────────┐
│ 종목 │ 비중 │ 투자한도 │ 투입금 │ 평가 │ 수익률 │
│ BTC │ 35% │ 5,000,000 │ 4.2M │ 4.5M │ +7.1% │
│ ETH │ 20% │ 3,000,000 │ 2.8M │ 2.5M │ -10.7% │
│ [+ 종목 추가] 한도·전략 연결 │
└──────────────────────────────────────────────────────────────┘
```
**행 동작**
- 행 클릭 → 차트/주문 패널 종목 전환
- 한도 편집 → `PATCH /paper/allocations/{symbol}`
- 비활성 토글 → 해당 종목 신규 매수 차단
### 5.4 모의투자 대시보드 (기존 `PaperTradingPage` 개편)
```
┌──────────┬─────────────────────────────┬──────────────┐
│ 좌측 │ 중앙 │ 우측 │
│ · 전략 │ [투자금 관리] [성과차트] │ 매수/매도 │
│ · 설정 │ 보유·KPI │ 호가/체결 │
└──────────┴─────────────────────────────┴──────────────┘
```
- 좌측 **설정** → §5.2 로 링크 또는 임베드
- 중앙 **투자금 관리** → §5.3
- 우측 **TradeOrderPanel** — 종목별 `available_buy_krw` 표시
### 5.5 거래내역 화면
| 컬럼 | 설명 |
|------|------|
| 체결시각 | |
| 종목 | |
| 구분 | 매수(빨강) / 매도(파랑) — `--gc-trade-buy/sell` |
| 가격·수량·금액 | |
| 수수료 | |
| 실현손익 | 매도 행만 |
| 출처 | MANUAL / STRATEGY |
| 체결 후 예수금 | `cash_after` |
**필터**: 기간, 종목, side, source · **페이징** API
### 5.6 가상투자와의 동기화
| 가상투자 UI | DB 매핑 |
|-------------|---------|
| 투자대상 목록 | `gc_paper_symbol_allocation` (symbol, strategy_id, pinned) |
| 카드 매수/매도 | `/paper/orders` + 한도 검증 |
| 자동매매 | `paper_auto_trade_*` + 종목 `max_invest_krw` |
**권장**: 가상투자 `targets` 저장 시 백엔드 `PUT /paper/allocations/bulk` 로 동기화 (localStorage는 캐시만).
---
## 6. 기능 설계
### 6.1 API (신규·확장)
| 메서드 | 경로 | 설명 |
|--------|------|------|
| GET | `/paper/summary` | 기존 + `orderableCash`, `initialCapitalSnapshot`, `allocations[]` |
| GET | `/paper/allocations` | 종목별 한도·투입·가용·수익률 |
| PUT | `/paper/allocations` | bulk upsert (가상투자 동기) |
| PATCH | `/paper/allocations/{symbol}` | 단일 종목 한도 수정 |
| GET | `/paper/trades` | `?symbol=&side=&from=&to=&page=` |
| GET | `/paper/ledger` | 자금 원장 |
| GET | `/paper/snapshots/daily` | 자산 추이 |
| POST | `/paper/orders` | **한도·orderable 검증** 추가 |
| POST | `/paper/reset` | reset_log + ledger + allocation 정책에 따라 재생성 |
### 6.2 매수 검증 플로우
```mermaid
sequenceDiagram
participant UI as 주문 UI
participant API as PaperTradingService
participant DB as gc_paper_*
UI->>API: POST /paper/orders BUY
API->>DB: load account + allocation + position
API->>API: needKrw = price×qty + fee
alt needKrw > orderable_cash
API-->>UI: 400 가용현금 부족
else needKrw > available_buy_krw
API-->>UI: 400 종목 투자한도 초과
else
API->>DB: trade + position + ledger
API-->>UI: PaperTradeDto
end
```
### 6.3 매도 검증
- 보유 `quantity` ≥ 매도 수량
- 전량 매도 시 `realized_pnl_delta` 계산 → `account.realized_pnl` 누적
- (선택) 매도 후에도 allocation 행 유지, `invested_krw`=0
### 6.4 일별 스냅샷 배치
- 대상: `paper_trading_enabled` 이고 당일 체결/시세 변동 있는 계좌
- 입력: EOD mark prices (Ta4jStorage 또는 REST)
- UPSERT `gc_paper_daily_snapshot`
### 6.5 종목별 수익률 정의 (화면 표기 통일)
| 지표 | 정의 |
|------|------|
| **종목 평가수익률** | (mark×qty avg×qty) / (avg×qty) × 100 |
| **종목 실현손익** | 해당 종목 매도 체결의 `realized_pnl_delta` 합 |
| **계좌 총수익률** | (total_asset initial_capital_snapshot) / initial × 100 |
| **당일 수익률** | daily_snapshot.daily_return_pct |
---
## 7. 서비스·모듈 구조 (백엔드)
```
PaperTradingController
PaperTradingService ← 체결·잔고 (기존)
PaperAllocationService ← 종목별 한도 CRUD·검증
PaperLedgerService ← 원장 기록
PaperSnapshotService ← 일별 스냅샷·조회
PaperAnalyticsService ← MDD/샤프 (체결·스냅샷 기반, 기존 paperMetrics 이관 가능)
```
---
## 8. 구현 단계 (로드맵)
| 단계 | 범위 | 산출물 |
|------|------|--------|
| **P0** | DB V51 + allocation CRUD + 매수 한도 검증 | 마이그레이션, API, 단위 테스트 |
| **P1** | 투자금 관리 UI (보유+한도+KPI) | PaperTradingPage 탭, bulk sync 가상투자 |
| **P2** | cash_ledger + 거래내역 필터/페이징 | 원장 화면 |
| **P3** | daily_snapshot + 자산추이 차트 | 스케줄러 |
| **P4** | (선택) 미체결 주문 | gc_paper_order |
---
## 9. goldenChart 적용 시 유의사항
1. **단일 계좌**: `device_id` 기준 1계좌 유지 (상용 모의도 대부분 1:1).
2. **평가 시세**: summary는 `POST /paper/summary` + markPrices 패턴 유지.
3. **색상**: 매수 `#ef5350`, 매도 `#4dabf7` (`--gc-trade-buy/sell`).
4. **전략 평가와 분리**: Ta4j 시그널은 `gc_paper_*` 체결만 변경; 전략 DSL 평가는 기존 백엔드 단일 경로 유지.
5. **설정 이중화 해소**: `SettingsPage` 가상매매 = 전역 기본값, 모의투자 좌측 설정 = 동일 API 바인딩 또는 “설정으로 이동” 링크만.
---
## 10. 요약
| 계층 | 설계 요지 |
|------|-----------|
| **DB** | 계좌 + **종목별 allocation** + **cash 원장** + **일별 스냅샷** + 체결 확장 |
| **투자금 설정** | 전역 규칙·초기자본·초기화 (app_settings + reset_log) |
| **투자금 관리** | 총자산·예수금·종목별 한도/투입/수익률·비중 |
| **매수/매도** | 가용현금 ∧ 종목한도 ∧ (자동매매 %) 검증 후 체결 |
| **거래내역** | paper_trade + ledger, 필터·페이징 |
현재 V18 스키마는 **MVP 체결·보유** 에 적합하고, 상용 수준의 **종목별 투자금·원장·일별 성과** 는 §4.4 `V51` 마이그레이션과 §8 P0~P3 순으로 확장하는 것을 권장합니다.