Implement BAT-006: portfolio-hub ledger integration (REQ-017~019)

- REQ-017: LedgerPort contract (application/ledger_port.py) + portfolio-hub
  adapter + in-memory stub (adapters/ledger/), TREX_PORTFOLIO_HUB_* config
- REQ-018: fill→ledger recording in execution loop; FillStatus carries
  execution_id (deterministic synth) + fee; paper/kis adapters updated
- REQ-019: ledger outbox + retry worker (application/ledger_outbox.py),
  correlation_id idempotency, outcome vocabulary; 002_create_ledger_outbox.sql
- Tests: in-memory/portfolio-hub ledger, execution ledger hook, outbox,
  domain import sweep

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 22:57:31 +09:00
co-authored by Claude Opus 4.8
parent 6277d610ea
commit 13837acc60
20 changed files with 1224 additions and 15 deletions
@@ -0,0 +1,33 @@
-- BAT-006 / REQ-019: ledger outbox work-item store (dcy-004 OQ-2).
--
-- Each row is one fill that needs to be recorded in the portfolio-hub
-- ledger. `correlation_id` ({asset_id}:{order_id}:{execution_id}) is the
-- local dedup key — the same fill enqueued twice must NOT produce two
-- ledger entries. Outcomes are classified per dcy-004 FR-4:
-- completed | duplicate_completed | retryable_failure | conflict_blocked.
CREATE TABLE IF NOT EXISTS ledger_outbox (
correlation_id TEXT PRIMARY KEY,
asset_id TEXT NOT NULL,
order_id TEXT NOT NULL,
execution_id TEXT NOT NULL,
side TEXT NOT NULL CHECK (side IN ('buy','sell')),
qty NUMERIC(18, 4) NOT NULL,
price NUMERIC(18, 4) NOT NULL,
fee NUMERIC(18, 4) NOT NULL DEFAULT 0,
tax NUMERIC(18, 4) NOT NULL DEFAULT 0,
executed_at TIMESTAMPTZ,
status TEXT NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','completed','conflict_blocked')),
attempts INTEGER NOT NULL DEFAULT 0,
last_outcome TEXT
CHECK (last_outcome IS NULL OR last_outcome IN (
'completed','duplicate_completed','retryable_failure','conflict_blocked'
)),
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT (now() AT TIME ZONE 'UTC'),
updated_at TIMESTAMPTZ NOT NULL DEFAULT (now() AT TIME ZONE 'UTC')
);
CREATE INDEX IF NOT EXISTS idx_ledger_outbox_status_created
ON ledger_outbox (status, created_at);