diff --git a/docs/discovery/dcy-004_20260604_portfolio-hub-ledger-integration.md b/docs/discovery/dcy-004_20260604_portfolio-hub-ledger-integration.md index cb99892..815f7d4 100644 --- a/docs/discovery/dcy-004_20260604_portfolio-hub-ledger-integration.md +++ b/docs/discovery/dcy-004_20260604_portfolio-hub-ledger-integration.md @@ -78,9 +78,9 @@ ## 기능 요구사항 - FR-1: `LedgerPort.record_trade(asset, order_id, execution_id, side, qty, price, fee, tax, executed_at)` 추상화 + portfolio-hub 어댑터(`POST /v1/assets/{asset_id}/ledger/trades`) + 결정적 검증용 stub/in-memory 원장을 제공한다. -- FR-2: 집행 루프가 매수 체결(`pending_buy→holding`)·매도 체결(`pending_sell→closed`) 시 해당 거래를 원장에 기록한다(슬롯 `portfolio_id`/symbol + 체결 데이터 사용). -- FR-3: 원장 기록은 `execution_id` 로 멱등하다 — 동일 체결 재기록 시 portfolio-hub 중복/409(LedgerConflict)를 안전 처리하고 중복 원장 0. -- FR-4: portfolio-hub 일시 장애 시 원장 기록을 재시도/보류해 유실하지 않는다(슬롯 전이 일관성과 분리). +- FR-2: 집행 루프가 매수 체결(`pending_buy→holding`)·매도 체결(`pending_sell→closed`) 시 해당 거래를 원장에 기록한다. ledger path 의 `asset_id` 는 슬롯 `portfolio_id`(=asset_id, OQ-1 결정)를 사용하고 side/qty/price/체결데이터를 함께 보낸다. +- FR-3: 원장 기록은 correlation_id(`{asset_id}:{order_id}:{execution_id}`)로 멱등하다 — 동일 체결 재기록 시 로컬 dedup + portfolio-hub 중복/409(LedgerConflict)를 안전 처리하고 중복 원장 0. execution_id 는 결정적 합성(OQ-3). +- FR-4: 체결 시 원장 기록을 ledger outbox(work item)에 적재하고 재시도 루프가 portfolio-hub write 를 수행한다(orbit LedgerWriter 패턴). outcome = completed/duplicate_completed/retryable_failure/conflict_blocked. 슬롯 전이와 분리되어 일시 장애에도 유실 없이 결국 기록(conflict 는 운영자 해소). - FR-5: baseline 문서(runtime-flows/interface-contract/project-structure/data-model)를 ledger 레이어에 맞게 갱신한다. ## 비기능 요구사항 @@ -105,13 +105,11 @@ ## 리스크 - R-1 (High): 원장 기록 유실/중복 — 체결은 됐는데 원장 미기록(장애) 또는 중복 기록 시 회계 부정확. - - 완화 방안: execution_id 멱등(409/duplicate 안전 처리, FR-3) + 장애 재시도/보류(FR-4). stub 으로 결정적 테스트. -- R-2 (Medium): asset_id 해석 — portfolio-hub 경로는 `asset_id`(canonical) 인데 슬롯은 symbol/portfolio_id 보유. 매핑 불명확. - - 완화 방안: 매핑 규칙을 OQ-1 로 확정(portfolio_id/asset 매핑 또는 symbol→asset_id). + - 완화 방안(OQ-2/OQ-3): correlation_id 멱등(409/duplicate 안전) + ledger outbox 재시도 루프(무유실) + conflict_blocked 운영자 해소. stub 으로 결정적 테스트. +- R-2 (해소, OQ-1): asset_id 해석 — orbit 근거로 `asset_id == portfolio_id` 확정. 슬롯 `portfolio_id` 를 path 로 직접 사용(추가 매핑/조회 없음). - R-3 (Medium): 외부 의존 결합 — portfolio-hub 장애가 집행 경로를 막을 위험. - 완화 방안: 원장 기록을 슬롯 전이와 분리(전이는 우선, 기록은 재시도/보류). 어댑터 격리(NFR-1). -- R-4 (Medium): fee/tax/execution_id 출처 — 체결 데이터에 수수료/세금/체결ID가 항상 있지는 않음(paper vs KIS). - - 완화 방안: KIS 체결 조회/주문 응답에서 추출, 없으면 0/derived. OQ-3. +- R-4 (해소, OQ-3): fee/tax/execution_id 출처 — execution_id 결정적 합성(KIS 규칙/paper synth), fee 는 BrokerPort 계산(orbit calculate_fee), tax 기본 0(정밀 모델 후속). ## 가정 아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다. @@ -124,18 +122,17 @@ 답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다. - OQ-1: asset_id 해석 — portfolio-hub `{asset_id}` 를 슬롯의 무엇으로 채울지(`portfolio_id` == asset_id? symbol→asset_id 매핑? portfolio-hub get_assets 조회?). - - 상태: Open - - 처리 방안: 운영자/portfolio-hub 계약 확인. 기본안 검토: 슬롯 `portfolio_id` 를 asset_id 로 직접 사용 또는 symbol 기반 매핑. - - 종료 조건: asset_id 소스가 정해져 FR-1/FR-2 에 연결되면 Answered. -- OQ-2: 기록 내구성 방식 — fill 전이와 동일 경로 동기 기록(실패 시 재시도) vs 별도 ledger outbox(BAT-004 옵션 B 와 유사) 비동기. - - 상태: Open - - 처리 방안: 운영자 확인. 기본안: 동기 기록 + 실패 시 재시도/보류 마킹(슬롯에 ledger_recorded 플래그 또는 보류 큐). 강한 내구성 필요 시 outbox. - - 종료 조건: 내구성 방식이 정해져 FR-4 수용 기준에 연결되면 Answered. + - 상태: Answered (2026-06-04) + - 결정: **`asset_id == portfolio_id`** — 슬롯의 `portfolio_id` 를 ledger path param 으로 직접 사용. symbol 매핑/get_assets 조회 불필요. 근거: trex-orbit `app/domain/ledger.py`("asset_id == portfolio_id for this system"), migration 003(Order Execution 이 `{portfolio_id}` path 로 write-back, 매수 커밋 시 슬롯에 캐시), `coordinator.py`(`asset_id = asset_id or portfolio_id`). trex-core 는 이미 buy intent 가 `portfolio_id` 를 공급해 슬롯에 캐시하므로 추가 조회 없이 그대로 사용. + - 반영 위치: `# 5` FR-1·FR-2, `# 6` R-2(해소). buy intent 의 `portfolio_id` 필수 여부는 구현 시 결정(없으면 기록 skip 또는 거부). +- OQ-2: 기록 내구성 방식 — fill 전이와 동일 경로 동기 기록 vs 별도 ledger outbox 비동기. + - 상태: Answered (2026-06-04) + - 결정: **별도 ledger outbox(work item) + 재시도 루프** (orbit `LedgerWriter`/recovery_work_item 패턴 차용). 체결 시 ledger 기록을 outbox 에 적재(슬롯 전이와 분리 → 전이는 우선, 기록은 eventually-consistent), 루프가 portfolio-hub write 시도. correlation_id = `{asset_id}:{order_id}:{execution_id}` 로 dedup, outcome 어휘 = completed / duplicate_completed / retryable_failure(재시도) / conflict_blocked(409, 운영자 해소). 근거: orbit `app/domain/ledger.py` 가 동일 패턴으로 무유실+멱등을 보장. + - 반영 위치: `# 4`, `# 5` FR-3·FR-4, `# 6` R-1. (BAT-004 의 옵션 B outbox 와 유사 구조 — ledger 전용.) - OQ-3: 체결 데이터(execution_id/fee/tax) 출처 — KIS 체결조회/주문응답에서 추출 가능 범위, paper 더블의 합성 값. - - 상태: Open - - 처리 방안: KIS inquire-daily-ccld 응답 필드 확인 + paper 더블이 결정적 execution_id/fee 제공. - - 종료 조건: 필드 출처/기본값이 정해지면 Answered. - + - 상태: Answered (2026-06-04) + - 결정: **execution_id 는 결정적 합성** — KIS: `kis-daily-ccld:{ord_dt}:{ord_gno_brno}:{odno}:{tot_ccld_qty}`(orbit kis.py 규칙 차용), paper: 결정적 synth(예: `paper:{broker_order_id}:{filled_qty}`). **fee 는 BrokerPort fee 계산**(orbit `calculate_fee(side, amount)` 차용), **tax 는 기본 0**(정밀 세금 모델은 후속). FillStatus/브로커가 execution_id·fee 를 운반하도록 contract 확장. + - 반영 위치: `# 5` FR-1·FR-3, `# 6` R-4. (BrokerPort 에 fee 계산 + FillStatus.execution_id 추가는 구현 세부.) ### 오픈 질문 처리 기준 - `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다. - `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다. @@ -172,7 +169,7 @@ REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 - [ ] High 영향 리스크(R-1 원장 유실/중복) 처리 방침 확정 - 기준: execution_id 멱등 + 재시도/보류로 수용 가능한지 운영자 확인. - [ ] 모든 오픈 질문(OQ) `Open` 상태 없음 확인 - - 기준: OQ 목록에 `Open` 0개 또는 잔여 전부 `Deferred` (현재 OQ-1~OQ-3 Open) + - 기준: OQ 목록에 `Open` 0개 또는 잔여 전부 `Deferred` (OQ-1~OQ-3 전부 Answered → `Open` 0개) - [ ] Freeze 확정 담당자(CONFIRMED_BY) 지정 - 기준: 운영자가 confirm-discovery 시점에 CONFIRMED_BY 를 지정한다. @@ -189,12 +186,11 @@ REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 # 10. 사용자 결정 필요 항목 요약 사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다. -## DECIDE -- {{DECIDE_ASSET_ID_SOURCE}} — portfolio-hub `{asset_id}` 소스: 슬롯 portfolio_id 직접 vs symbol→asset_id 매핑 vs get_assets 조회 (OQ-1) -- {{DECIDE_LEDGER_DURABILITY}} — 기록 내구성: 동기+재시도/보류 vs 별도 ledger outbox (OQ-2) +- ~~asset_id 소스~~ → 결정됨: `asset_id == portfolio_id`(슬롯 portfolio_id 직접 사용, orbit 근거) (OQ-1, 2026-06-04) +- ~~기록 내구성~~ → 결정됨: 별도 ledger outbox + 재시도 루프(orbit LedgerWriter 패턴), correlation_id dedup, outcome 어휘 (OQ-2, 2026-06-04) ## CONFIRM -- {{CONFIRM_EXEC_DATA_SOURCE}} — 체결 데이터(execution_id/fee/tax) 출처(KIS 추출 + paper 합성) 확정 (OQ-3) +- ~~체결 데이터 출처~~ → 결정됨: execution_id 결정적 합성(KIS 규칙/paper) + fee BrokerPort 계산 + tax 0 (OQ-3, 2026-06-04) - {{CONFIRM_SCOPE_DEFER}} — 거래 외 트랜잭션·asset 동기화·reconciliation 을 후속으로 미루는 범위 경계 확정 ## DATA