# 모의투자 투자금·종목별 관리 설계 상용 주식/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 순으로 확장하는 것을 권장합니다.