ledger write 응답의 portfolio-hub 권위 cash/quantity 를 스냅샷에 write-back 해
매수/매도 직후 "수량 live·현금 stale" 비대칭 제거(option D 활성화). portfolio-hub#8
(*_after 응답) 동작 중 확인으로 그간 deferred 됐던 REQ-030 을 닫음.
거버넌스: 전부 change-req (신규 REQ 없음, dcy-018 OQ-3 a).
- REQ-017: record_trade → LedgerWriteOutcome{result, cash_balance_after,
asset_quantity_after}. 어댑터가 응답 root flat *_after 파싱(new+duplicate, 누락 None)
- REQ-019: LedgerWriter 에 SnapshotStore 주입 → new+duplicate write-back
(best-effort, *_after 절대값이라 멱등). LedgerOutbox.pending_cash_delta()
- REQ-029: 추정 현금 오버레이(_apply_cash_estimate, cash_estimated 라벨, OQ-4 i)
+ 슬롯 오버레이 유지(OQ-1 B). 미정산 outbox delta 기반, write-back 수렴
- REQ-030: option D 활성화 → Approved→Implemented
best-effort 격리(NFR-1): write-back/추정 실패가 ledger 멱등·청산 무차단.
배치 번호: 동시 커밋된 BAT-022(slot-state, dcy-016)와 충돌해 BAT-023 으로 배정.
검증: 단위 350 passed/35 skipped, 인프라 게이트(PG+RabbitMQ) 385 passed, ruff clean.
신규 test_ledger_option_d + adapter/PG 테스트. BAT-023 release-candidate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.4 KiB
3.4 KiB
BAT-023 Design
- Status: confirmed
- Batch: BAT-023
설계 목표
- ledger write 응답의 portfolio-hub 권위 cash/quantity 를 스냅샷에 write-back 해 "수량 live·현금 stale" 비대칭을 제거(option D 활성화). 공백 구간은 추정 현금으로 메우되 안전·멱등·best-effort 를 보장.
1. 응답 계약 확장 (REQ-017)
LedgerWriteOutcome{result: LedgerWriteResult, cash_balance_after: Optional[float], asset_quantity_after: Optional[float]}신설.record_trade반환 타입 변경.portfolio_hub.py: 2xx 응답 root flatcash_balance_after/asset_quantity_after파싱(_opt_num, 누락/형변환 실패 시 None). new(201)/duplicate(200) 모두. 409→Conflict, 404/400/422→Permanent(dcy-015) 예외 유지.in_memory.py:cash_after/quantity_after주입 파라미터 → outcome 에 실어 반환(테스트용).
2. write-back (REQ-019/030, option D)
LedgerWriter(snapshot_store=None)주입._process가new·duplicateoutcome 후_writeback(trade, outcome)호출._writeback: snapshot_store None 또는*_afterNone 이면 skip. 아니면update_cash_position(trade.asset_id, cash_after, qty_after). try/except 로 격리(실패는 WARN, ledger outcome·청산 무차단, NFR-1).- duplicate 도 write-back(OQ-2 b):
*_after가 절대 권위값이라 멱등(중복 적용 위험 없음, NFR-3). 409/422 는 예외라 자연 skip.
3. 추정 현금 오버레이 (REQ-029 / OQ-4)
- source = 미정산 outbox delta(슬롯 아님): 슬롯 기반이면 write-back 후에도 슬롯이 holding 이라 double-count. 미정산(pending) outbox 행은 정확히 "fill∼write-back 공백" 집합.
trade_cash_delta(trade): buy → -(qtyprice + fee + tax), sell → +(qtyprice - fee - tax)(코어 근사).LedgerOutbox.pending_cash_delta() -> {asset_id: Σdelta}(InMemory: pending 행 합 / PG: SQL SUM CASE). 백오프 무관 전체 pending._apply_cash_estimate(payload, ledger_outbox)(snapshot 핸들러): pending delta 있는 자산에cash_balance += delta+cash_estimated:true. best-effort(예외 시 응답 불변).- write-back 완료 → 행이 pending 이탈 → delta 0 → 추정 사라지고 store 는 권위값. 자연 수렴, double-count 없음.
4. 수량 source (REQ-029, OQ-1 B)
- 슬롯 오버레이(
build_snapshot_envelope) 변경 없음 — lifecycle 중 슬롯이 코어 권위 즉시값(불변식 A). write-back 은 현금 + 슬롯 밖 base 수량을 신선화. 슬롯↔write-back 불일치는 REQ-031 포지션 정합 경고로 노출(숨기지 않음).
5. daemon 배선
- 생성 순서:
SnapshotStore를LedgerWriter보다 먼저 생성(write-back target). LedgerWriter(snapshot_store=self.snapshot_store), snapshot 핸들러ledger_outbox=self.outbox.
6. baseline 문서 (REQ-030 정합 포함)
- interface-contract:
LedgerWriteOutcome/*_after/write-back/envelopecash_estimated. - runtime-flows: ledger writer write-back 단계 + snapshot 응답 추정 오버레이.
- REQ-030 Status Approved→Implemented(파일 + srs/index register).
테스트 설계
- adapter:
*_after파싱(new/duplicate/누락). - writer: write-back new/duplicate/skip(None)/best-effort(store 예외)/store 미주입; pending_cash_delta 합; estimate overlay + write-back 후 수렴.
- PG 게이트:
pending_cash_deltaSQL. - 회귀: record_trade 반환 타입 변경에 따른 기존 ledger 테스트(outcome.result) 정합 — 기존 테스트 더블/소비처 전환.