feat: consolidate ledger trade writes into portfolio_ledger

- 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
This commit is contained in:
2026-06-05 20:55:24 +09:00
parent 2470c0a00c
commit 44f2102d26
23 changed files with 1254 additions and 115 deletions
@@ -0,0 +1,41 @@
# BAT-016 Implementation
- Status: implemented
- Batch: BAT-016
## Plan Summary
- 순서: REQ-036(마이그레이션 + ensure_schema) → REQ-037(repository/도메인/응답/회계) → REQ-038(deprecated)
- 각 REQ 후 부분 회귀, batch 마지막 통합 회귀를 수행했다.
## Changed Files
- `migrations/versions/20260605_0025_ledger_consolidation.py`
- `app/repositories/postgres.py`
- `app/repositories/portfolio_repository.py`
- `app/domain/models.py`
- `app/domain/services.py` call-site compatible model usage retained through `LedgerEntry.action` compatibility
- `app/schemas/v1.py`
- `app/api/v1_portfolios.py`
- `app/api/portfolios.py`
- `tests/test_v1_portfolios_ledger.py`
- `tests/test_v1.py`
- `tests/test_daemon_assignment.py`
- `tests/test_auth.py`
## Execution Log
1. `portfolio_ledger` schema was consolidated to carry trade-write identifiers/state directly: `ledger_entry_id`, `order_id`, `execution_id`, `side`, `executed_at`, `cash_balance_after`, and `asset_quantity_after`.
2. Legacy `ledger_trades` references were removed from application code; `ensure_schema()` now migrates old `action` data into `side`, drops `action`, and drops `ledger_trades` when present.
3. `LedgerEntry` became the canonical domain object for recent trades and authoritative ledger-trade writes.
4. `POST /v1/portfolios/{portfolio_id}/ledger/trades` now writes to `portfolio_ledger`, supports idempotent lookup by broker identifiers, computes cash/security balances, and returns accounting state.
5. Legacy `/portfolios/{portfolio_id}/buy` and `/portfolios/{portfolio_id}/sell` routes were marked `deprecated=True` and emit `Deprecation: true` on legacy responses/errors.
6. Regression tests were updated to assert the consolidated model and deprecation header behavior.
## Validation
- Targeted regression:
- `.venv/bin/python -m pytest -q tests/test_domain_services.py tests/test_v1_portfolios_ledger.py tests/test_daemon_assignment.py tests/test_v1.py`
- Result: `47 passed in 3.66s`
- Full suite:
- `.venv/bin/python -m pytest -q`
- Result: `373 passed, 3 warnings in 14.78s`
## Remaining Risks
- Timescale hypertable constraints can require time-column-aware unique/primary indexes. The runtime consolidation avoids unsupported primary/unique constraints on `portfolio_ledger`; idempotency is enforced in repository logic by looking up `(portfolio_id, order_id, execution_id)` before insert.