- add ledger consolidation migration and remove ledger_trades usage - return portfolio state after v1 ledger trade writes - deprecate legacy buy/sell endpoints with deprecation headers - add BAT-016, DCY-006, and REQ-036~038 documentation - update repository models and tests for unified ledger entries
8.4 KiB
8.4 KiB
REQ-037: 신 path 회계 책임 단일화 + 응답에 portfolio 상태 포함
- Status: Approved
- Type: Interface
- Priority: High
- Owner: Justin Ko
Intent
POST /v1/portfolios/{portfolio_id}/ledger/trades를 portfolio 회계의 단일 권위 endpoint로 단일화한다. ledger 기록 시 portfoliocash_balance/asset_quantity를 트랜잭션 갱신하고, 응답에 거래 반영 후 권위 상태를 포함해 trex-core daemon의 drift를 원천 차단한다(issue #8).- 현재 audit-only 흐름(
insert_ledger_trade가ledger_tradesINSERT만 수행)을 portfolio_ledger 단일 원장 기록 + portfolio 갱신 + 응답 확장으로 재정의한다.
Requirement
- 신 path handler(app/api/v1_portfolios.py:514-588
record_ledger_trade_v1)가 다음을 단일 DB 트랜잭션으로 수행한다(dcy-006 FR-6):- portfolios row를
SELECT ... FOR UPDATE로 잠근다(FR-7, lost update 방지). - 회계 공식 적용: side="buy" →
cash_balance -= qty*price + fee + tax,asset_quantity += qty; side="sell" →cash_balance += qty*price - fee - tax,asset_quantity -= qty. - 잔고/수량 부족 거부 검증을 적용하지 않는다 — 음수
cash_balance/asset_quantity모두 허용(dcy-006 OQ-3). 음수 검증을 제거한 회계 헬퍼를 ledger 흐름 전용으로 추출하거나 inline 구현(기존apply_buy/apply_sell은 레거시 전용으로 보존, R-8). portfolio_ledger에 INSERT(REQ-036 신 스키마 사용). broker 식별자order_id/execution_id/side/executed_at+ 갱신 후 잔고 snapshot(cash_balance_after,asset_quantity_after,total_value_after) 기록.- portfolios 테이블 UPDATE(
cash_balance,asset_quantity,last_trade_at,updated_at).
- portfolios row를
- duplicate 감지(dcy-006 FR-15):
ON CONFLICT (portfolio_id, order_id, execution_id) WHERE order_id IS NOT NULL DO NOTHING로 전환(기존ON CONFLICT ON CONSTRAINT uq_ledger_trades_idempotency대체). PostgreSQL partial index inference 사용. status="new"(신규 row): 거래 반영 후 권위 상태를 응답한다(FR-2).status="duplicate"(재호출): ledger row 재생성 없이 duplicate 호출 시점의 portfolios 테이블 현재 권위 상태를 SELECT로 조회해 응답한다(FR-3, dcy-006 OQ-4 (b)).LedgerTradeResponse(app/schemas/v1.py)에cash_balance_after: float,asset_quantity_after: float두 필드를 응답 root에 flat으로 비파괴 추가한다(FR-1, dcy-006 OQ-2 (b)). 기존 응답 필드는 모두 보존.- 기존 인가(daemon 3단계: token daemon_id == portfolio.managed_by_daemon_id), idempotency, status 의미를 보존한다(FR-4).
- 도메인 모델 통합(FR-13):
LedgerEntry와LedgerTradedataclass를LedgerEntry단일로 통합.LedgerTrade삭제. broker 필드(ledger_entry_id,order_id,execution_id,side,executed_at)를LedgerEntry에 흡수. - repository 통합(FR-14):
insert_ledger_trade를 portfolio_ledger insert + portfolios UPDATE 단일 트랜잭션 함수로 재정의(또는 통합)._row_to_ledger_trade는_row_to_ledger_entry와 통합.
Acceptance Criteria
- Given service token + daemon 배정 active portfolio(cash=1,000,000, qty=0) + buy 10@80,000 fee 100, When
POST /v1/portfolios/{id}/ledger/trades, Then HTTP 201 +status="new"+ 응답cash_balance_after=199900(1,000,000 - 800,000 - 100) +asset_quantity_after=10. DB portfolios도 동일 값으로 갱신. - Given AC-1 직후 같은 (portfolio_id, order_id, execution_id)로 재호출, When 동일 payload, Then HTTP 200 +
status="duplicate"+ 응답cash_balance_after/asset_quantity_after는 현재 portfolios 권위 값. portfolio_ledger row는 1건 유지(재생성 없음). - Given sell로 보유 수량보다 큰 수량 또는 cash를 초과하는 buy, When 호출, Then HTTP 201(거부하지 않음) + 음수
cash_balance_after또는 음수asset_quantity_after반환(음수 허용, OQ-3). - Given 기존 응답 필드(ledger_entry_id, portfolio_id, order_id, execution_id, side, quantity, price, fee_amount, tax_amount, executed_at, recorded_at, status), When AC-1 응답 확인, Then 모두 보존되고
cash_balance_after/asset_quantity_after만 추가된다. - Given service token
daemon_id IS NULL또는 daemon mismatch, When 호출, Then 기존 인가 동작(HTTP 403 fail-closed) 보존. - Given codebase grep
grep -rn "ledger_trades\|insert_ledger_trade\|class LedgerTrade\b" app/, When 본 REQ + REQ-036 구현 후, Thenledger_trades테이블 INSERT 코드 및LedgerTradedataclass 참조가 0건(LedgerEntry 단일). - Given 동시 ledger write 2건(같은 portfolio), When 트랜잭션 실행, Then
SELECT FOR UPDATErow lock으로 lost update 없이 두 거래가 순차 반영된다(회귀 테스트).
Impacted Area
- Docs:
docs/interface-contract.md—POST /v1/portfolios/{id}/ledger/trades회계 동작 + 응답 schema(cash_balance_after/asset_quantity_after) + status별 동작 명문화docs/runtime-flows.md— ledger write flow에 회계 적용 + portfolio 갱신 + 트랜잭션 lock 단계 + portfolio_ledger 단일 원장 명문화
- Modules:
app/api/v1_portfolios.py:record_ledger_trade_v1— 통합 함수 호출 + 응답 확장app/repositories/postgres.py—insert_ledger_trade재정의(portfolio_ledger insert + portfolios UPDATE 단일 트랜잭션 + FOR UPDATE + ON CONFLICT partial),_row_to_ledger_*통합app/repositories/portfolio_repository.py— interface 시그니처 갱신app/domain/models.py—LedgerEntry단일화,LedgerTrade삭제app/domain/services.py또는 신규 — 음수 검증 제거한 ledger 전용 회계 헬퍼 추출app/schemas/v1.py—LedgerTradeResponse에cash_balance_after/asset_quantity_after추가
- Tests:
- 갱신
tests/test_v1_portfolios_ledger.py— AC-1~7 (응답 신규 필드 + 회계 + duplicate + 음수 + 인가 보존 + 동시성) - 갱신
tests/test_v1.py,tests/test_daemon_assignment.py— LedgerEntry 통합 + 응답 schema 반영
- 갱신
Notes
- 본 REQ는 dcy-006 FR-1/2/3/4/6/7/13/14/15 산출물 + FR-5(interface-contract/runtime-flows) 일부 흡수.
- REQ-036(마이그레이션) 선행 필요 — portfolio_ledger 신 스키마 위에서 동작.
- 음수 허용은 신 path 한정. 레거시 buy/sell은 기존 거부 정책 유지(REQ-038 deprecated 대상).
- 별도 REL(REL-003 등)로 분리 배포(dcy-006 A-4).
- Source: dcy-006 FR-1/2/3/4/6/7/13/14/15, OQ-1~4/6/7.
Change Log
CHG-20260605-02
- Date: 2026-06-05
- Author: Justin Ko
Change Summary
- Status: Proposed → Approved
Intent
- 승인 게이트 통과: 필수 필드 모두 충족, AC-1~7 구체성(수치 회계 예시 + 음수 허용 + duplicate + 동시성) 확인 완료, blocker 없음.
Acceptance Criteria Delta
- No change
Impacted Area Delta
- No change
Delivery Trace Delta
- Batches: none (미정)
- Releases: none (미정 — REL-003 등 분리 예정)
Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: promote-to-approved
- Follow-up Action: batch 계획 편입; REQ-036 선행 의존성 확인
CHG-20260605-01
- Date: 2026-06-05
- Author: Justin Ko
Change Summary
- 최초 생성 — dcy-006 confirm-discovery 후 draft-req 실행. dcy-006 회계 책임 단일화 + 응답 확장 산출물.
Intent
- 신 path를 portfolio 회계 단일 권위 endpoint로 재정의하고 응답에 거래 후 권위 상태를 포함해 trex-core drift 차단.
Acceptance Criteria Delta
- Added:
- AC-1~7 최초 작성
Impacted Area Delta
- Docs: docs/interface-contract.md, docs/runtime-flows.md — 최초 지정
- Modules: app/api/v1_portfolios.py, app/repositories/postgres.py, app/repositories/portfolio_repository.py, app/domain/models.py, app/domain/services.py, app/schemas/v1.py — 최초 지정
- Tests: tests/test_v1_portfolios_ledger.py, tests/test_v1.py, tests/test_daemon_assignment.py — 최초 지정
Delivery Trace Delta
- Batches: none (미정)
- Releases: none (미정 — REL-003 등 분리 예정)
Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 계획 편입; REQ-036 선행 의존성 확인
Validation Plan
- pytest TestClient로 AC-1~7 검증; 동시성은 회귀 또는 staging concurrent test