Preserve ledger venue provenance (#39)
* feat: preserve ledger venue provenance * fix: satisfy ledger test lint ratchet * fix: advance image migration inventory
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
---
|
||||
id: "dcy-0009"
|
||||
status: confirmed
|
||||
---
|
||||
|
||||
# Broker ledger venue provenance preservation
|
||||
|
||||
## Problem
|
||||
|
||||
`trex-core`는 broker가 확인한 실제 체결 거래소를 `LedgerTrade.venue`에, 최초 주문 라우팅 거래소를 `LedgerTrade.order_venue`에 구분하여 durable ledger outbox까지 보존한다. 그러나 `PortfolioHubLedger._payload()`가 두 값을 HTTP payload에서 누락하고, `portfolio-hub`의 broker ledger request·저장소·응답 모델에도 대응 필드가 없다. 이 때문에 core 내부에서 보존된 venue provenance가 portfolio-hub 전달 경계에서 소실된다.
|
||||
|
||||
기대 상태는 core의 nullable actual execution venue와 original order venue를 추정·변환 없이 portfolio-hub에 전달하고, 기존 broker ledger 행·manual ledger·corporate action·구버전 client의 동작을 깨뜨리지 않으면서 저장·조회하는 것이다.
|
||||
|
||||
## Evidence
|
||||
|
||||
- `E-001` GitHub Issue #32 `https://github.com/fourblocks2020/portfolio-hub/issues/32`는 `LedgerTrade.venue -> execution_venue`, `LedgerTrade.order_venue -> order_venue` mapping, nullable historical compatibility, duplicate metadata 보강, Hub-first rollout과 venue-only 범위를 구현 계획으로 정한다.
|
||||
- `E-002` `trex-core` revision `bbdacb121c25a7539f6f9f7fe0559fc03d4542eb`의 `trex_core/application/ledger_port.py`는 `LedgerTrade.venue: Optional[str]`와 `LedgerTrade.order_venue: str`를 별도 필드로 정의한다.
|
||||
- `E-003` 같은 revision의 `trex_core/adapters/persistence/postgresql.py`와 migration `024_add_ledger_order_venue.sql`은 durable outbox에 `venue`와 `order_venue`를 별도 저장·복원하지만, `trex_core/adapters/ledger/portfolio_hub.py::PortfolioHubLedger._payload()`는 두 값을 HTTP payload에 포함하지 않는다.
|
||||
- `E-004` `portfolio-hub` revision `567d662913335a90d0de51b8e0e961b6b166d543`의 `app/schemas/v1.py::LedgerTradeRequest`·`LedgerTradeResponse`, `app/domain/models.py::LedgerEntry`, `app/repositories/portfolio_repository.py`와 `app/repositories/postgres.py`에는 execution/order venue 계약이 없다.
|
||||
- `E-005` 현재 Alembic head의 `portfolio_ledger`에는 두 venue column이 없으며 `app/db/migrations.py`와 Alembic migration이 빈 DB와 legacy DB bootstrap을 함께 소유한다.
|
||||
- `E-006` portfolio-hub active AC-0032·AC-0034·AC-0035·AC-0037은 unified ledger read, nullable source-specific facts, authorization·manual compatibility와 joined ledger 안정성을 소유하지만 broker venue의 POST validation·persistence·duplicate 보강을 명시하지 않는다. 따라서 새 observable contract는 `create` 대상이고 기존 AC는 의미를 바꾸지 않는 `satisfy` 대상이다.
|
||||
- `E-007` trex-core active AC-0019는 original `order_venue`와 nullable actual execution venue의 durable 분리, `(order_venue=SOR, venue=NULL)` 보존, 무추론 원칙과 기존 `LedgerWriter` 전달 경로를 이미 소유한다. Core 변경은 기존 AC의 의미를 바꾸지 않는 satisfy 구현이다.
|
||||
|
||||
## Scope
|
||||
|
||||
- `S-001` portfolio-hub에 새 active AC를 `create`하여 broker ledger request가 nullable `execution_venue` (`KRX`, `NXT`)와 nullable `order_venue` (`KRX`, `NXT`, `SOR`)를 수용하고 다른 값과 `execution_venue=SOR`를 거부하는 계약을 소유한다.
|
||||
- `S-002` `portfolio_ledger`에 nullable venue column과 allowlist constraint를 additive migration으로 추가하고 historical row를 NULL로 유지한다. 빈 DB·legacy DB migration apply/replay와 실제 PostgreSQL round-trip을 검증한다.
|
||||
- `S-003` 새 active AC를 `create`하여 broker ledger insert, duplicate lookup, domain mapping, POST 응답과 unified GET 응답이 두 venue를 추정·변환 없이 저장·반환하는 계약을 소유한다.
|
||||
- `S-004` 동일 `(portfolio_id, order_id, execution_id)` duplicate는 portfolio row lock으로 기존 동시성 경계를 유지하면서 기존 ledger row도 `FOR UPDATE`로 선택한다. 같은 transaction에서 기존 economic payload 비교를 먼저 수행해 불일치면 아무 metadata도 갱신하지 않고 `409`로 중단한다. Economic payload가 같을 때만 모든 venue의 non-null 충돌을 먼저 검사하고, 충돌이 없으면 NULL column만 incoming non-null 값으로 한 번에 보강한다. Incoming NULL은 기존 값을 지우지 않으며 이 보강은 ledger row를 재생성하거나 cash·quantity·snapshot 회계를 다시 적용하지 않는다.
|
||||
- `S-005` 기존 active AC-0032·AC-0034·AC-0035·AC-0037을 `satisfy`하여 source-neutral ordering/filter/cursor, nullable source-specific representation, owner-only authorization, manual/corporate-action 및 WAC/PnL compatibility를 유지한다.
|
||||
- `S-006` trex-core는 active AC-0019 아래 `PortfolioHubLedger._payload()`가 기존 `LedgerTrade.venue`를 `execution_venue`, 기존 `LedgerTrade.order_venue`를 `order_venue`로 전송하도록 변경한다. producer, `LedgerTrade`, outbox schema와 broker routing은 변경하지 않는다.
|
||||
- `S-007` 배포 호환 경계는 portfolio-hub 선배포 후 trex-core adapter 배포이며, 구버전 core payload와 historical/manual/corporate-action row는 venue가 NULL인 상태로 계속 유효하다.
|
||||
- `S-008` 검증은 portfolio-hub schema/API/repository/PostgreSQL migration과 concurrency/idempotency, trex-core exact adapter payload·outbox reload/retry, 양 저장소 교차 contract `201 new -> 200 duplicate -> GET /ledger`를 포함한다.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- `OS-001` `broker_ordered_at`, `core_observed_filled_at` 또는 다른 timestamp provenance.
|
||||
- `OS-002` trex-core `LedgerTrade`, producer, **broker execution adapter**, durable outbox schema 또는 주문 routing 변경. `PortfolioHubLedger` HTTP ledger transport adapter의 두 기존 field 직렬화는 `S-006` 범위다.
|
||||
- `OS-003` historical venue 추정·backfill, 특히 `order_venue`나 시간대로 actual execution venue를 추론하는 동작.
|
||||
- `OS-004` ledger detail endpoint, idempotency key, broker 주문 실행·라우팅 정책 변경.
|
||||
- `OS-005` production migration·deployment·runtime restart·실제 broker 주문.
|
||||
|
||||
## Risks and Unknowns
|
||||
|
||||
### Risks
|
||||
|
||||
- `R-001` mixed-version rollout에서 새 core가 구 Hub에 먼저 배포되면 추가 payload field가 거부되거나 유실될 수 있다. Hub를 먼저 배포하고 기존 payload omission을 허용한다.
|
||||
- `R-002` duplicate metadata 보강이 기존 회계 transaction path를 재실행하면 잔액·수량·snapshot이 중복 반영될 수 있다. row lock과 단일 transaction에서 provenance-only update를 분리하고 회계 불변 회귀를 실행한다.
|
||||
- `R-003` repository query의 positional mapping과 joined corporate-action projection에 column을 추가할 때 기존 mapping이 어긋날 수 있다. 명시 projection·mapping 검토와 실제 PostgreSQL mixed-row 회귀로 방지한다.
|
||||
- `R-004` concurrent duplicates가 서로 다른 non-null venue를 보강하면 last-write-wins로 provenance가 변조될 수 있다. 동일 row를 잠그고 NULL-only update와 non-null conflict를 transaction 안에서 판정한다.
|
||||
|
||||
### Assumptions
|
||||
|
||||
- `A-001` Issue #32에 적힌 venue-only 구현 계획과 사용자의 현재 구현 지시가 제품 범위의 근거이며, 추가 timestamp나 broker lifecycle 변경은 승인되지 않았다.
|
||||
- `A-002` trex-core active AC-0019의 기존 producer/outbox 계약은 변경 없이 유지되고, 이번 core 변경은 transport 누락을 복구하는 satisfy 작업이다.
|
||||
|
||||
### Open Questions
|
||||
|
||||
없음. Issue #32와 현재 source에서 API naming, 값 allowlist, NULL semantics, duplicate policy, migration policy, rollout 순서와 제외 범위가 모두 결정되어 있다.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
id: "line-0009"
|
||||
status: discovery
|
||||
---
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: "req-0009"
|
||||
status: approved
|
||||
discovery: "dcy-0009"
|
||||
criteria:
|
||||
create:
|
||||
- "ac-0039"
|
||||
- "ac-0040"
|
||||
- "ac-0041"
|
||||
update: []
|
||||
retire: []
|
||||
satisfy:
|
||||
- "ac-0032"
|
||||
- "ac-0034"
|
||||
- "ac-0035"
|
||||
- "ac-0037"
|
||||
---
|
||||
|
||||
# Broker ledger venue provenance preservation
|
||||
|
||||
## Objective
|
||||
|
||||
`trex-core`가 이미 durable하게 구분하는 actual execution venue와 original order-routing venue를 portfolio-hub의 broker ledger write·storage·read 경계까지 손실 없이 보존한다. Unknown actual venue와 SOR routing을 정상 조합으로 유지하고, 기존 client·historical/manual/corporate-action row·회계·조회 계약과 혼합 버전 배포의 하위호환성을 보장한다.
|
||||
|
||||
## Scope
|
||||
|
||||
- Broker ledger request에 optional nullable `execution_venue`와 `order_venue`를 추가한다.
|
||||
- `execution_venue`는 `KRX`, `NXT`, NULL만 허용하고 `order_venue`는 `KRX`, `NXT`, `SOR`, NULL만 허용한다.
|
||||
- `portfolio_ledger`에 두 nullable column과 같은 allowlist constraint를 additive migration으로 추가한다. 기존 row는 NULL로 유지한다.
|
||||
- Broker ledger insert·domain mapping·POST response와 unified GET broker item이 저장값을 추정·변환 없이 반환한다.
|
||||
- 동일 `(portfolio_id, order_id, execution_id)` duplicate는 portfolio row lock에 더해 기존 ledger row를 `FOR UPDATE`로 선택한다. 같은 transaction에서 기존 economic payload를 먼저 비교해 불일치면 metadata 변경 없이 `409`로 중단한다. Economic payload가 같을 때 모든 venue의 non-null 충돌을 먼저 검사하고, 충돌이 없을 때만 NULL→non-null venue를 하나의 metadata-only update로 보강한다. Incoming NULL은 기존 non-null을 지우지 않는다.
|
||||
- Metadata-only duplicate 보강은 ledger row, portfolio cash·asset quantity·snapshot, WAC/PnL 또는 corporate-action state를 다시 적용하지 않는다.
|
||||
- 기존 active AC-0032·AC-0034·AC-0035·AC-0037을 의미 변경 없이 satisfy하여 unified ordering/filter/cursor, canonical nullable representation, owner authorization, manual/corporate-action 및 joined ledger compatibility를 유지한다.
|
||||
- trex-core는 active AC-0019 아래 기존 `LedgerTrade.venue`를 `execution_venue`, `LedgerTrade.order_venue`를 `order_venue`로 HTTP payload에 전달한다. Core producer·domain·outbox schema는 변경하지 않는다.
|
||||
- 실제 PostgreSQL migration·round-trip·concurrent duplicate와 core adapter payload/reload, 교차 저장소 `201 new -> 200 duplicate -> GET /ledger` contract를 검증한다.
|
||||
|
||||
## Constraints
|
||||
|
||||
- `SOR`는 order routing provenance이며 actual execution venue로 저장하거나 추론하지 않는다.
|
||||
- Unknown actual venue는 NULL로 보존하고 `order_venue`, 시간 또는 historical default에서 KRX/NXT를 추론하지 않는다.
|
||||
- 기존 idempotency key와 기존 경제적 payload conflict 판정은 유지한다.
|
||||
- 추가 request field는 optional이어야 하며 구버전 core/client payload가 계속 유효해야 한다.
|
||||
- Historical, manual, corporate-action row는 두 venue가 NULL이어도 유효해야 한다.
|
||||
- Hub-first, core-adapter-second 배포 순서를 문서화하되 production rollout은 수행하지 않는다.
|
||||
- Normative Design Documents: 없음. 이 REQ와 create AC-0039~AC-0041 및 satisfy 대상 active AC가 normative specification을 소유한다.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- `broker_ordered_at`, `core_observed_filled_at` 또는 다른 timestamp provenance.
|
||||
- trex-core `LedgerTrade`, producer, **broker execution adapter**, outbox table/schema 또는 routing policy 변경. `PortfolioHubLedger` HTTP ledger transport adapter의 기존 venue 직렬화는 Scope에 포함한다.
|
||||
- Historical venue 추정·backfill.
|
||||
- 새로운 ledger detail endpoint 또는 idempotency key 변경.
|
||||
- Production migration·deployment·restart·실제 broker 주문.
|
||||
Reference in New Issue
Block a user