Expert team identified 12 gaps across 6 scenarios. Prioritized into 3 phases: Critical (rebalance apply, strategy-to-portfolio link), Important (signal holdings, DC limits, benchmark, signal PnL), and Nice-to-have (comparison UI, walk-forward, undo). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 KiB
시나리오 갭 분석 결과 및 구현 계획
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: DC형 퇴직연금 퀀트 포트폴리오 관리의 6대 시나리오에서 발견된 Gap을 해소하여 end-to-end 사용 흐름을 완성한다.
Architecture: 기존 FastAPI + Next.js 구조를 유지하면서, 누락된 API 엔드포인트와 프론트엔드 UI를 추가한다. 백엔드 변경은 기존 모델/서비스를 확장하고, 프론트엔드는 기존 컴포넌트 패턴을 따른다.
Tech Stack: FastAPI, SQLAlchemy, Next.js 15, React 19, TypeScript, Radix UI, Recharts
전문가 팀 갭 분석 결과
종합 매핑표
| # | 요구사항 | 상태 | 우선순위 |
|---|---|---|---|
| 1.1 | 퇴직연금 포트폴리오 생성 | IMPLEMENTED | - |
| 1.2 | DC형 위험자산 70% 제한 경고 | MISSING | Important |
| 1.3 | 보유종목 일괄 입력 UI | PARTIAL (API만 존재) | Important |
| 1.4 | 실시간 평가금액 표시 | IMPLEMENTED | - |
| 2.1 | 전략 결과 → 포트폴리오 목표 연결 | MISSING | Critical |
| 2.2 | 팩터 점수/투명성 | IMPLEMENTED | - |
| 2.3 | DC 투자가능 종목 필터 | MISSING | Nice-to-have |
| 2.4 | 전략 결과 비교 UI | PARTIAL | Nice-to-have |
| 3.1 | 전 전략 백테스트 지원 | IMPLEMENTED | - |
| 3.2 | Out-of-sample/Walk-forward | MISSING | Nice-to-have |
| 3.3 | 백테스트 에러 처리 | PARTIAL | Nice-to-have |
| 4.1 | 리밸런싱 결과 → 일괄 거래 생성 | MISSING | Critical |
| 4.2 | 최소 거래 금액 고려 | MISSING | Nice-to-have |
| 4.3 | 리밸런싱 후 위험자산 비율 체크 | MISSING | Important |
| 5.1 | 신호별 PnL 추적 | MISSING | Important |
| 5.2 | 실행된 신호 취소/수정 | MISSING | Nice-to-have |
| 5.3 | 실행 모달에 현재 보유량 표시 | MISSING | Important |
| 5.4 | 포지션 사이징 가이드 | PARTIAL | Nice-to-have |
| 6.1 | TWR 수익률 계산 | IMPLEMENTED | - |
| 6.2 | 포트폴리오 벤치마크 비교 | PARTIAL (백테스트만) | Important |
| 6.3 | 스냅샷 데이터 충분성 | IMPLEMENTED | - |
| 6.4 | 실현/미실현 수익 구분 | MISSING | Important |
| 6.5 | 대시보드 전체 현황 | IMPLEMENTED | - |
전문가별 핵심 의견
Q (퀀트 전략가):
- 전략 실행 → 포트폴리오 반영 연결이 완전히 끊겨 있음. 전략을 실행해도 결과를 수동으로 옮겨야 하므로 시스템의 핵심 가치가 반감됨.
- 신호 기반 매매의 성과를 추적할 수 없어 전략 개선이 불가능함.
PM (포트폴리오 매니저):
- 리밸런싱 계산 후 "적용" 기능이 없어, 계산 결과를 보고 다시 수동으로 거래를 하나씩 입력해야 함. 핵심 워크플로우의 마지막 단계가 빠져있음.
- 실현/미실현 수익 구분이 없으면 세금 신고나 성과 분석이 부정확함.
P (DC형 퇴직연금 전문가):
- DC형 퇴직연금의 위험자산 70% 제한은 법적 요건. 경고 없이 운용하면 퇴직연금사업자의 제재 대상이 될 수 있음. 최소한 경고 수준은 필수.
- 단, 이 시스템은 개인용이므로 제재라기보다 자기 관리 차원의 알림이면 충분.
SE (시스템 엔지니어):
- 데이터 파이프라인과 백테스트 엔진은 안정적으로 구현됨.
- 신호 실행 시 동시성 문제 (같은 신호 중복 실행) 검토 필요하나, 단일 사용자 시스템이므로 당장 Critical은 아님.
DA (Devil's Advocate):
- "전략 → 포트폴리오 → 리밸런싱 → 성과 추적"의 핵심 루프가 중간중간 끊겨 있음. 각 기능이 개별적으로는 존재하지만 연결 흐름이 없으면 Excel 스프레드시트보다 나을 게 없다.
- 특히 리밸런싱 결과 적용과 전략→목표 연결은 이 시스템의 존재 이유. 이것 없이는 단순 조회 도구에 불과.
- 신호 실행 취소가 없으면 실수 한 번에 데이터 정합성이 깨질 수 있음. 최소한 거래 삭제 기능은 필요.
- 벤치마크 비교가 백테스트에만 있고 실제 포트폴리오에 없는 것은 "과거는 분석하면서 현재는 분석 못하는" 아이러니.
구현 우선순위
DA의 의견을 반영하여, **"핵심 루프 완성"**에 집중합니다.
Phase 1: Critical - 핵심 루프 연결 (Task 1-2)
Phase 2: Important - 실용성 강화 (Task 3-6)
Phase 3: Nice-to-have - 고도화 (별도 계획)
Task 1: 리밸런싱 결과 일괄 적용 API + UI
리밸런싱 계산 결과에서 "적용" 버튼을 누르면 매수/매도 거래가 일괄 생성되는 기능.
Files:
- Modify:
backend/app/api/portfolio.py(리밸런싱 적용 엔드포인트 추가) - Modify:
backend/app/schemas/portfolio.py(적용 요청/응답 스키마) - Modify:
frontend/src/app/portfolio/[id]/rebalance/page.tsx(적용 버튼 + 확인 모달) - Test:
backend/tests/e2e/test_rebalance_flow.py
Step 1: Write the failing test
# backend/tests/e2e/test_rebalance_flow.py - 기존 파일에 추가
def test_apply_rebalance(client, auth_headers, db_session):
"""리밸런싱 결과를 적용하면 거래가 일괄 생성된다."""
# Setup: portfolio with targets and holdings
# ...
response = client.post(
f"/api/portfolios/{portfolio_id}/rebalance/apply",
json={
"items": [
{"ticker": "005930", "action": "buy", "quantity": 10, "price": 70000},
{"ticker": "000660", "action": "sell", "quantity": 5, "price": 150000},
]
},
headers=auth_headers,
)
assert response.status_code == 201
data = response.json()
assert len(data["transactions"]) == 2
assert data["transactions"][0]["tx_type"] == "buy"
assert data["transactions"][1]["tx_type"] == "sell"
Step 2: Run test to verify it fails
Run: backend/.venv/bin/pytest backend/tests/e2e/test_rebalance_flow.py::test_apply_rebalance -v
Expected: FAIL (endpoint does not exist)
Step 3: Add backend schema
# backend/app/schemas/portfolio.py에 추가
class RebalanceApplyItem(BaseModel):
ticker: str
action: str # "buy" or "sell"
quantity: int = Field(..., gt=0)
price: FloatDecimal = Field(..., gt=0)
class RebalanceApplyRequest(BaseModel):
items: List[RebalanceApplyItem]
class RebalanceApplyResponse(BaseModel):
transactions: List[TransactionResponse]
holdings_updated: int
Step 4: Add backend endpoint
# backend/app/api/portfolio.py에 추가
@router.post("/{portfolio_id}/rebalance/apply", response_model=RebalanceApplyResponse, status_code=status.HTTP_201_CREATED)
async def apply_rebalance(
portfolio_id: int,
data: RebalanceApplyRequest,
current_user: CurrentUser,
db: Session = Depends(get_db),
):
"""리밸런싱 결과를 적용하여 거래를 일괄 생성한다."""
portfolio = _get_portfolio(db, portfolio_id, current_user.id)
transactions = []
for item in data.items:
tx_type = TransactionType(item.action)
transaction = Transaction(
portfolio_id=portfolio_id,
ticker=item.ticker,
tx_type=tx_type,
quantity=item.quantity,
price=item.price,
executed_at=datetime.utcnow(),
memo="리밸런싱 적용",
)
db.add(transaction)
# Update holding (기존 add_transaction 로직 재사용)
holding = db.query(Holding).filter(
Holding.portfolio_id == portfolio_id,
Holding.ticker == item.ticker,
).first()
if tx_type == TransactionType.BUY:
if holding:
total_value = (holding.quantity * holding.avg_price) + (item.quantity * item.price)
new_quantity = holding.quantity + item.quantity
holding.quantity = new_quantity
holding.avg_price = total_value / new_quantity if new_quantity > 0 else 0
else:
holding = Holding(
portfolio_id=portfolio_id,
ticker=item.ticker,
quantity=item.quantity,
avg_price=item.price,
)
db.add(holding)
elif tx_type == TransactionType.SELL:
if not holding or holding.quantity < item.quantity:
raise HTTPException(status_code=400, detail=f"Insufficient quantity for {item.ticker}")
holding.quantity -= item.quantity
if holding.quantity == 0:
db.delete(holding)
transactions.append(transaction)
db.commit()
for tx in transactions:
db.refresh(tx)
return RebalanceApplyResponse(
transactions=transactions,
holdings_updated=len(transactions),
)
Step 5: Run test to verify it passes
Run: backend/.venv/bin/pytest backend/tests/e2e/test_rebalance_flow.py::test_apply_rebalance -v
Expected: PASS
Step 6: Add frontend "적용" 버튼 + 확인 모달
frontend/src/app/portfolio/[id]/rebalance/page.tsx에 추가:
- 리밸런싱 계산 결과 테이블 아래에 "리밸런싱 적용" 버튼
- 클릭 시 확인 모달 표시 (매수/매도 요약)
- 사용자가 각 항목의 실제 체결가를 수정 가능
- "적용" 시 POST /api/portfolios/{id}/rebalance/apply 호출
- 성공 시 포트폴리오 상세 페이지로 이동
Step 7: Commit
git add backend/app/api/portfolio.py backend/app/schemas/portfolio.py \
frontend/src/app/portfolio/\[id\]/rebalance/page.tsx \
backend/tests/e2e/test_rebalance_flow.py
git commit -m "feat: add bulk rebalance apply endpoint and UI"
Task 2: 전략 결과 → 포트폴리오 목표 연결
전략 실행 결과에서 "포트폴리오에 적용" 버튼으로 목표 배분을 설정하는 기능.
Files:
- Modify:
frontend/src/app/strategy/multi-factor/page.tsx(적용 버튼 추가) - Modify:
frontend/src/app/strategy/quality/page.tsx(적용 버튼 추가) - Modify:
frontend/src/app/strategy/value-momentum/page.tsx(적용 버튼 추가) - Test: 프론트엔드 E2E 또는 수동 테스트
Step 1: 전략 결과 페이지에 "포트폴리오에 적용" 버튼 추가
각 전략 결과 페이지에:
- 포트폴리오 선택 드롭다운 (GET /api/portfolios)
- "목표 배분으로 적용" 버튼
- 클릭 시: 전략 결과의 종목 + 동일 비중을 PUT /api/portfolios/{id}/targets로 전송
- 기존 목표를 덮어쓸지 확인 모달
Step 2: 공통 컴포넌트 생성
// frontend/src/components/strategy/apply-to-portfolio.tsx
// 포트폴리오 선택 + 적용 버튼 + 확인 모달
// 3개 전략 페이지에서 공유
Step 3: 각 전략 페이지에 컴포넌트 배치
Step 4: 수동 테스트
전략 실행 → 결과에서 포트폴리오 선택 → 적용 → 포트폴리오 목표 확인
Step 5: Commit
git add frontend/src/components/strategy/apply-to-portfolio.tsx \
frontend/src/app/strategy/multi-factor/page.tsx \
frontend/src/app/strategy/quality/page.tsx \
frontend/src/app/strategy/value-momentum/page.tsx
git commit -m "feat: add strategy results to portfolio targets flow"
Task 3: 신호 실행 모달에 현재 보유량 표시
Files:
- Modify:
frontend/src/app/signals/page.tsx
Step 1: 실행 모달 열 때 선택된 포트폴리오의 보유 종목 조회
포트폴리오 선택 시 GET /api/portfolios/{id}/holdings 호출하여 해당 종목의 현재 보유 수량을 표시.
// 모달 내 신호 정보 섹션에 추가:
// "현재 보유: {quantity}주 (평균단가: {avg_price}원)" 또는 "미보유"
Step 2: 매도 신호일 때 보유 수량 기반 기본값 설정
매도/부분매도 신호의 경우, 수량 기본값을 현재 보유량(또는 보유량의 50%)으로 설정.
Step 3: Commit
git add frontend/src/app/signals/page.tsx
git commit -m "feat: show current holdings in signal execute modal"
Task 4: DC형 위험자산 비율 경고
Files:
- Modify:
backend/app/api/portfolio.py(비율 계산 유틸) - Modify:
frontend/src/app/portfolio/[id]/page.tsx(경고 배너)
Step 1: 백엔드에 위험자산 비율 계산 추가
포트폴리오 상세 응답에 risk_asset_ratio 필드 추가. ETF 중 채권형/MMF를 안전자산, 나머지를 위험자산으로 분류. pension 유형일 때만 계산.
Step 2: 프론트엔드에 경고 배너 표시
pension 유형 포트폴리오에서 위험자산 비율이 70%를 초과하면 상단에 경고 배너 표시.
Step 3: Commit
git commit -m "feat: add risk asset ratio warning for DC pension portfolios"
Task 5: 포트폴리오 벤치마크 비교
Files:
- Modify:
backend/app/api/snapshot.py(returns 엔드포인트에 벤치마크 추가) - Modify:
backend/app/services/returns_calculator.py(벤치마크 데이터 조회) - Modify:
frontend/src/app/portfolio/[id]/history/page.tsx(벤치마크 차트)
Step 1: returns API에 벤치마크 수익률 추가
GET /api/portfolios/{id}/returns 응답에 벤치마크(KOSPI) 누적수익률을 함께 반환.
Step 2: 프론트엔드 수익률 차트에 벤치마크 라인 추가
기존 포트폴리오 수익률 라인 차트에 KOSPI 수익률 라인을 오버레이.
Step 3: Commit
git commit -m "feat: add benchmark comparison to portfolio returns"
Task 6: 신호 성과 추적 (Signal PnL)
Files:
- Modify:
backend/app/models/signal.py(executed_price, exit_price, pnl 필드 추가) - Create: Alembic migration
- Modify:
backend/app/api/signal.py(신호 성과 조회 엔드포인트) - Modify:
backend/app/schemas/signal.py(성과 관련 필드) - Modify:
frontend/src/app/signals/page.tsx(성과 컬럼 추가)
Step 1: Signal 모델에 실행 관련 필드 추가
# backend/app/models/signal.py에 추가
executed_price = Column(Numeric(12, 2)) # 실제 체결가
executed_quantity = Column(Integer) # 실행 수량
executed_at = Column(DateTime) # 실행 시점
Step 2: 신호 실행 시 필드 업데이트
POST /api/signal/{id}/execute에서 executed_price, executed_quantity, executed_at 저장.
Step 3: 신호 응답에 성과 필드 포함
SignalResponse에 executed_price, executed_quantity를 추가하여 프론트엔드에서 표시.
Step 4: 프론트엔드 신호 테이블에 체결가/수량 컬럼 추가
executed 상태 신호에 체결가, 수량 표시.
Step 5: Commit
git commit -m "feat: track signal execution details for PnL analysis"
Phase 3: Nice-to-have (별도 계획 필요)
아래 항목은 핵심 루프 완성 후 별도 계획으로 진행:
| 항목 | 설명 |
|---|---|
| 전략 결과 비교 UI | 멀티팩터/퀄리티/밸류모멘텀 결과를 나란히 비교 |
| DC 투자가능 종목 필터 | ETF-only 필터, DC 적격 종목만 표시 |
| Walk-forward 분석 | 백테스트 기간 분할 검증 |
| 최소 거래 단위 | 리밸런싱 시 최소 거래 금액 고려 |
| 신호 실행 취소 | 실행된 신호 되돌리기 (거래 삭제 포함) |
| 실현/미실현 수익 분리 | 매도 시 실현 수익 계산 및 별도 추적 |
| 포지션 사이징 가이드 | 신호 실행 시 추천 수량/최대 포지션 표시 |