diff --git a/docs/releases/index.md b/docs/releases/index.md index 32bd423..0e8bcf7 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -9,3 +9,4 @@ | REL ID | Profile | Status | Included Batch | Date | Doc | | --- | --- | --- | --- | --- | --- | | REL-001 | app-service | released | BAT-001~016 (전체 통합) | 2026-05-21 | [rel-001](./rel-001_20260521_v1-api-full-surface.md) | +| REL-002 | app-service | confirmed | BAT-017 (dcy-007 M1: 거래내역+실현PnL API) | 2026-06-19 | [rel-002](./rel-002_20260619_trade-history-and-pnl-api.md) | diff --git a/docs/releases/rel-002_20260619_trade-history-and-pnl-api.md b/docs/releases/rel-002_20260619_trade-history-and-pnl-api.md new file mode 100644 index 0000000..145ab41 --- /dev/null +++ b/docs/releases/rel-002_20260619_trade-history-and-pnl-api.md @@ -0,0 +1,139 @@ +# REL-002: 일일 거래내역 + 실현 PnL 조회 API (dcy-007 M1) + +- Status: confirmed +- Profile: app-service +- Included Batch: BAT-017 + +## Scope + +- Included: + - BAT-017 (REQ-039, REQ-040, REQ-041, REQ-042): dcy-007 M1 fast-track — 일일 거래내역 + 실현 PnL 조회 API 신설 + - REQ-039: GET /v1/portfolios/{id}/trades (date 단일/from+to 범위 필터, read_token 이상 인증, 403/404/422) + - REQ-040: GET /v1/portfolios/{id}/pnl/realized (from+to 필수, symbol 선택 필터, WAC 기반 실현 PnL, data_completeness_warning, cost_basis_from) + - REQ-041: WAC cost_basis 산출 로직 — app/domain/pnl.py (순수 Python, DB 무의존, R-1 완화 경고 로직) + - REQ-042: 테스트 커버리지 — test_wac_calculation.py(20건), test_v1_portfolios_trades.py(12건), test_v1_portfolios_pnl.py(18건) + - Freeze 커밋: `bd34450` (main HEAD, 2026-06-18) +- Excluded: + - 미실현 PnL (현재가 연동) — dcy-007 OQ-2 Out of Scope + - 전체 portfolio 집계 PnL 뷰 — dcy-007 OQ-2 Out of Scope + - DB 마이그레이션 없음 (기존 portfolio_ledger 스키마로 조회 가능) + - 페이지네이션 — 이번 범위 외 이연 + - 성능 테스트 (p95 500ms) — TimescaleDB 통합/성능 환경에서 별도 검증 필요 + - BAT-001~016 (REL-001 포함 완료) + +## Rollout Plan + +1. **사전 확인**: 코드 freeze 커밋 `bd34450` main HEAD 일치 확인 +2. **DB 마이그레이션 없음**: REL-002는 기존 `portfolio_ledger` 스키마(alembic head = `20260605_0025`, REL-001 적용 완료)를 그대로 사용. 추가 마이그레이션 불필요. +3. **portfolio-hub 서비스 배포**: 신규 이미지(freeze 커밋 `bd34450`) rolling restart + - 신규 엔드포인트: `GET /v1/portfolios/{id}/trades` (REQ-039) + - 신규 엔드포인트: `GET /v1/portfolios/{id}/pnl/realized` (REQ-040) + - WAC domain 로직: `app/domain/pnl.py` 신규 (REQ-041) + - 기존 엔드포인트 영향 없음 (additive 변경) +4. **배포 후 smoke 검증**: 하기 Verification Checklist 배포 직후 항목 실행 + +## Rollback Plan + +- **트리거 조건**: portfolio-hub 배포 후 `GET /health` 실패, 신규 API 500 대규모 발생, 기존 API 회귀 발생 +- **절차**: + 1. portfolio-hub → 이전 이미지(REL-001 freeze `6b812b6`) rolling restart + 2. DB 마이그레이션 없으므로 DB 롤백 불필요 + 3. 기존 API (`/v1/portfolios`, `/v1/assets`, `/v1/tags`, `/v1/portfolios/{id}/ledger/trades` 등) 정상 동작 확인 +- **롤백 영향**: 신규 엔드포인트 2개(`/trades`, `/pnl/realized`)만 제거됨. 기존 운영 기능 영향 없음. additive 변경이라 롤백 리스크 낮음. + +## Verification Checklist + +- **배포 전**: + - [ ] freeze 커밋 `bd34450`이 배포 대상 이미지에 포함됨 확인 + - [ ] pytest 전체 (BAT-017 포함) 427 passed 확인: 50 (BAT-017 신규) + 377 (기존 회귀 0) — 2026-06-18 기준 + - [ ] DB 마이그레이션 불필요 확인 (alembic head `20260605_0025` 그대로) + - [ ] REL-001 운영 인스턴스 health 정상 확인 (배포 베이스라인) +- **배포 직후 (portfolio-hub)**: + - [ ] `GET /health` → HTTP 200 + `db_enabled: true` 확인 + - [ ] `GET /v1/portfolios/{id}/trades?date=YYYY-MM-DD` (read_token 이상) → HTTP 200 + 항목 필드(`trade_id`, `side`, `quantity`, `price`, `executed_at`, `fee_amount`, `tax_amount`) 확인 (REQ-039 AC-1) + - [ ] `GET /v1/portfolios/{id}/trades` (날짜 파라미터 누락) → HTTP 422 확인 (REQ-039 AC-3) + - [ ] `GET /v1/portfolios/{id}/pnl/realized?from=YYYY-MM-DD&to=YYYY-MM-DD` (read_token 이상) → HTTP 200 + `total_realized_pnl`, `cost_basis_from`, `items`, `data_completeness_warning` 필드 확인 (REQ-040 AC-1) + - [ ] `GET /v1/portfolios/{id}/pnl/realized` (from/to 누락) → HTTP 422 확인 (REQ-040 AC-3) + - [ ] 비소유자 토큰으로 `/trades` 또는 `/pnl/realized` 호출 → HTTP 403 확인 (REQ-039 AC-6, REQ-040 AC-7) + - [ ] 존재하지 않는 portfolio_id로 호출 → HTTP 404 확인 (REQ-039 AC-7, REQ-040 AC-8) + - [ ] OpenAPI `/docs` 에서 신규 엔드포인트 2개 노출 확인 (REQ-039 AC-8, REQ-040 AC-9) + - [ ] 기존 `GET /v1/portfolios`, `POST /v1/portfolios/{id}/ledger/trades`, `GET /health` 정상 동작 회귀 없음 확인 + +## Known Issues (QC PASS-WITH-NOTES 비차단 잔여) + +1. **symbol 필터 edge 테스트 공백**: `GET /v1/portfolios/{id}/pnl/realized?symbol=X` 호출 시 `symbol_param != portfolio_symbol` 케이스(다른 symbol 필터 요청)에 대한 자동 테스트가 불완전. AC-2는 충족이나 완전 커버 아님. 별도 cleanup batch에서 처리 권장. +2. **HTTP_422_UNPROCESSABLE_ENTITY deprecated**: FastAPI 최신 버전에서 `HTTP_422_UNPROCESSABLE_ENTITY` → `HTTP_422_UNPROCESSABLE_CONTENT` 경고. 기존 코드 패턴 유지(breaking change 없음). 별도 cleanup batch에서 처리 권장. +3. **성능 AC-9/AC-10 미검증**: `GET /v1/portfolios/{id}/trades` 및 `/pnl/realized` p95 500ms — TimescaleDB 인덱스 환경에서 별도 실행 필요. 환경 제약으로 이번 릴리즈 검증 범위 외(코드 결함 아님). +4. **symbol 컬럼 부재**: `portfolio_ledger`에 symbol 컬럼 없음. 현재 portfolio 단위(1 portfolio = 1 symbol) 가정으로 동작. 멀티 symbol portfolio 지원 시 별도 설계 필요. + +## Release Execution Evidence + +- Release Status Transition Target: released + +### Deployment Facts + +- Executed At: + - (운영팀 핸드오프 후 기록 — 예정 2026-06-22 KST) +- Completed At: + - (배포 완료 후 기록) +- Environment: + - portfolio-hub 운영 인스턴스 (port 8002) +- Operator: + - (배포 수행자 기록) +- Revision or Artifact: + - 코드 freeze 커밋: `bd34450` (main HEAD, 2026-06-18) +- Commands or Procedure: + - DB 마이그레이션 없음 (alembic head `20260605_0025` 유지) + - portfolio-hub 이미지 빌드 (freeze 커밋 `bd34450`) → rolling restart + +### Profile-Specific Checks + +- app-service: + - Health Endpoint: + - (배포 후 기록 — `GET /health` HTTP 200 + `db_enabled: true` 확인) + - Startup Log Fields: + - (배포 후 기록) + - Request Log Fields: + - (배포 후 기록) + - Smoke or Manual Verification: + - **배포 전 자동 테스트 evidence (2026-06-18)**: + - BAT-017 신규 테스트: 50 passed (test_wac_calculation.py 20건 + test_v1_portfolios_trades.py 12건 + test_v1_portfolios_pnl.py 18건) — pytest 실행 출력: `50 passed, 8 warnings in 3.43s` + - 기존 회귀 테스트: 377 passed, 0 failed — 회귀 없음 + - 합계: 427 passed, 0 failed + - 독립 QC (`qc-reviewer`, 2026-06-18): **PASS-WITH-NOTES** — 50 passed 독립 재현 + 전체 427 passed 회귀 없음, WAC=11,000/realized=199,300 단위+e2e 검증, R-1 경고필드(buy없음·oversell·완전이력 null) 정상, NFR-3 격리(domain DB import 0) 확인, 스코프·DB 마이그레이션 무, 위생 이상 없음. + - 운영팀 주간 테스트: 4개 서비스 전수 통과 (2026-06-19 사장님 확인) + - 배포 후 smoke: (운영팀 핸드오프 후 기록) + +### Rollback Outcome + +- Rollback Required: + - (배포 후 기록) +- Rollback Trigger: + - none (사전) +- Rollback Action: + - none (사전) + +### Release Decision Evidence + +- Released Decision: + - confirmed 단계 도달. released 전환 조건: 운영 배포 완료 + health endpoint 확인 + 배포 후 smoke 통과 후 recorded. +- Rationale: + - 오늘(2026-06-19) 확보된 evidence: (1) 코드 freeze 커밋 `bd34450` push 완료, (2) BAT-017 QC PASS-WITH-NOTES (독립 qc-reviewer 50 passed 재현, 2026-06-18), (3) 운영팀 주간 테스트 통과 사장님 확인(2026-06-19). 실제 배포(2026-06-22 예정)와 배포 후 smoke evidence는 아직 없으므로 released로 전환하지 않는다. 배포 완료 후 운영팀이 본 문서의 Deployment Facts + Profile-Specific Checks + Rollback Outcome을 기록하고 팀장이 released 전환을 승인한다. +- Recorded By: + - portfolio-hub-dev (팀장 지시 M1 릴리즈 REL 작성, 2026-06-19) +- Recorded At: + - 2026-06-19 KST + +## Feedback Handoff + +- Observation Summary: + - (배포 후 운영 관찰 기록) +- Discovery Input: + - symbol 필터 edge (다른 symbol 필터 요청 케이스) 자동 테스트 보강 — cleanup batch 후보 + - HTTP_422_UNPROCESSABLE_ENTITY deprecated cleanup — cleanup batch 후보 + - 성능 테스트 (p95 500ms) — TimescaleDB 인덱스 환경 구성 후 별도 검증 Discovery 후보 + - 멀티 symbol portfolio 지원 (symbol 컬럼 portfolio_ledger 추가) — 별도 Discovery 후보 +- REQ Input: + - (운영 관찰 후 기록) +- Change Request Input: + - (운영 관찰 후 기록)