# BAT-017 Implementation ## Status: Complete ## Changed Files ### New Files | File | Purpose | | --- | --- | | `app/domain/pnl.py` | WAC realized PnL calculation (REQ-041) — pure Python, no DB dependency | | `tests/test_wac_calculation.py` | WAC unit tests — 20 tests, DB-free (REQ-042) | | `tests/test_v1_portfolios_trades.py` | GET /trades integration tests — 12 tests (REQ-042) | | `tests/test_v1_portfolios_pnl.py` | GET /pnl/realized integration tests — 18 tests (REQ-042) | ### Modified Files | File | Changes | | --- | --- | | `app/schemas/v1.py` | Added `date` import; added `TradeHistoryItem`, `TradeHistoryResponse`, `RealizedPnlItem`, `RealizedPnlResponse` | | `app/api/v1_portfolios.py` | Added `date`, `datetime`, `timedelta`, `timezone` imports; added `calculate_realized_pnl` import; added schema imports; added `_parse_date_param()` helper; added `list_portfolio_trades()` endpoint (REQ-039); added `get_realized_pnl()` endpoint (REQ-040) | | `app/repositories/postgres.py` | Added `list_trades_by_date()` method (REQ-039); added `get_all_trades_for_pnl()` method (REQ-040/041) | | `app/repositories/portfolio_repository.py` | Added `list_trades_by_date()` and `get_all_trades_for_pnl()` abstract method signatures | | `docs/batches/index.md` | Added BAT-017 register row | ### Batch Documents | File | Purpose | | --- | --- | | `docs/batches/bat-017_.../index.md` | Batch index | | `docs/batches/bat-017_.../planning.md` | Planning | | `docs/batches/bat-017_.../design.md` | Design | | `docs/batches/bat-017_.../implementation.md` | This file | | `docs/batches/bat-017_.../verification.md` | Verification | ## Key Implementation Decisions ### WAC 계산 함수 위치 - `app/domain/pnl.py` 신규 — domain 계층 (REQ-041 Notes: domain 계층 권장) - `app/services/portfolio_service.py` 수정 없음 — WAC 함수는 domain에 독립 배치 ### `_parse_date_param` 날짜 형식 강제 - Python 3.11+ `date.fromisoformat()`은 `YYYYMMDD`(대시 없음)도 수용함 - REQ-039 AC-4 준수를 위해 `re.fullmatch(r"\d{4}-\d{2}-\d{2}", value)` 사전 검증 추가 ### `get_all_trades_for_pnl` 쿼리 설계 - WAC 정확성 확보를 위해 집계 기간(`from`/`to`) 제약 없이 portfolio의 모든 buy/sell 이력 조회 - Python domain 함수에서 `from_dt`/`to_dt` 파라미터로 sell 집계 필터 적용 ### `list_trades_by_date` 날짜 경계 - `date` 단일: `[date 00:00 UTC, date+1day 00:00 UTC)` 반개구간 - `to_` 포함 경계: `to_ + timedelta(days=1)` exclusive upper bound 적용 ### data_completeness_warning 경고 - running_qty=0에서 sell 발생 또는 sell qty > running_qty 시 경고 필드 설정 - 계산은 중단하지 않고 최선 추정 계속 (REQ-041 AC-3/AC-4) ### 서비스/클라이언트 토큰 접근 제한 - 두 엔드포인트 모두 `token_type in ("service", "client")` → 403 - DB 토큰 또는 env 토큰(read 이상)만 접근 가능