Files
KArtSell.Aegis/docs/CURRENT/SLICE_SPECS/VS-14-SLICE_SPEC.md
T
kjh2064 b1e38ac374 feat: Phase 3 J/K/L (Sell Decision, Trade Execution, Portfolio Reconciliation) + fix pre-existing build/boot breakage
Completes VS-10/VS-12/VS-14 and makes the solution and Host actually
build and boot for the first time on this branch (main did not build
before this commit).

Root-cause fixes required to reach a green build/boot (not scoped to
J/K/L but blocking any verification of it):
- Restore Polly PackageVersion accidentally deleted from
  Directory.Packages.props (broke KArtSell.Host).
- Remove MediatR dependency from Compliance/VS-04 (package was never
  installed; ICommand/ICommandHandler/IMediator never existed) and
  wire Endpoint -> Handler directly per this repo's convention.
- Migrate FastEndpoints v5 API calls (SendOkAsync/SendAsync/
  SendCreatedAtAsync/SendNotFoundAsync, Description().WithName()) to
  the v7 Send.* fluent API across ~10 endpoint files.
- Fix migrations 0036/0038/0039/0040: rewritten from invalid T-SQL
  (`IF NOT EXISTS ... BEGIN ... END`) to idiomatic Postgres
  (`CREATE TABLE/INDEX IF NOT EXISTS`) — these could not apply to any
  fresh database before this fix.
- Collapse 3 duplicate cross-cutting abstractions that shadowed the
  BuildingBlocks versions and caused type-mismatch compile errors:
  IKrxDataService, IOutboxWriter (ReconcileTradeHandler), IClock
  (ApprovalWorkflow/ApprovalPolicy).
- Inject IClock (BuildingBlocks.Time) in place of direct
  DateTime.Now/UtcNow across 19 files to satisfy the architecture
  test AGENTS.md#DateTime-abstraction rule (13/13 architecture tests
  now pass, was 12/13).
- Register all new and previously-unregistered slices in
  Program.cs DI (SellDecision, TradeExecution, PortfolioReconciliation,
  Compliance, Features/ApprovalWorkflow) — the Host had never
  successfully completed a boot with this code present.
- Disable ("[DontRegister]") the older, route-colliding
  ApprovalWorkflow/ (Workstream H) endpoint set in favor of
  Features/ApprovalWorkflow/ (Workstream G, matches the documented
  Features/<Slice>/ convention); kept for its existing test coverage.
  See TECH_DEBT-017 for the follow-up decision needed.

Verified: dotnet build 0 errors/0 warnings; architecture tests 13/13;
unit tests 54/54 + 18/18; integration tests 34/36 (2 failures are a
local test-DB migration-journal/schema mismatch, not a code defect);
Host boots cleanly and registers all 34 endpoints.

New tech debt recorded: DEBT-017 (duplicate VS-03 implementation),
DEBT-018 (outbox write not co-transactional with entity write in
TradeExecution/PortfolioReconciliation), DEBT-019 (duplicate
BuildingBlocks-shadowing abstractions, partially resolved).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 19:53:38 +09:00

7.2 KiB
Raw Blame History

VS-14: Portfolio Reconciliation (Sell Decision → Trade → Holdings)

Vertical Slice: VS-14 (Portfolio Reconciliation)
Version: 1.0 COMPLETE
Date: 2026-08-07
Owner: Data Architecture + Finance
Status: READY FOR IMPLEMENTATION
Depends On: K (trade execution), uses VS-03 (approval) + VS-04 (audit)


📋 User Story

As a portfolio manager / compliance officer
I want to reconcile portfolio holdings after trade execution
So that we can verify execution accuracy, track cost basis, and detect discrepancies

Acceptance Criteria:

  • Holdings updated after trade execution (quantity, cost basis)
  • Cost basis tracked: weighted average, FIFO/LIFO support
  • Gain/loss calculated (unrealized, realized on sale)
  • Mismatches detected: quantity, price, timing, settlement variance
  • Audit trail immutable (reconciliation_logs INSERT-only)
  • API endpoints: GET holdings state, GET mismatch discrepancies
  • Daily/weekly reconciliation reporting

🎯 Non-Goals

  • Tax lot assignment strategies (use FIFO by default)
  • Real-time market valuation (use T+1 settlement assumption)
  • Corporate actions (splits, dividends) handling (deferred)
  • Multi-account consolidation (single account only for v1.0)

📊 Reconciliation Flow

Trade Executed (from VS-12)
  ↓
Extract trade details: quantity, price, settlement date
  ↓
Validate against approval (from VS-03)
  ↓
Update holdings: quantity ± executed
  ↓
Calculate cost basis: weighted average
  ↓
Calculate gain/loss: (market_value - cost_basis)
  ↓
Detect mismatches: quantity, price, timing, settlement
  ↓
Log reconciliation event (immutable, INSERT-only)
  ↓
Generate reconciliation report (daily/weekly)
  ↓
Alert on discrepancies (for manual review)

📊 Data Schema

holdings (Current Portfolio State)

CREATE TABLE holdings (
    id UUID PRIMARY KEY,
    security_id UUID NOT NULL REFERENCES financial_security_master.securities(id),
    quantity INT NOT NULL DEFAULT 0,
    weighted_avg_cost DECIMAL(15,2) NOT NULL DEFAULT 0,
    total_cost_basis DECIMAL(18,2) NOT NULL DEFAULT 0,
    market_value DECIMAL(18,2),  -- T+1 settlement basis
    unrealized_gain_loss DECIMAL(18,2),  -- (market_value - cost_basis)
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    correlation_id UUID NOT NULL,
    revision INT NOT NULL DEFAULT 1
);

CREATE INDEX idx_holdings_security_id ON holdings(security_id);
CREATE INDEX idx_holdings_correlation_id ON holdings(correlation_id);

reconciliation_logs (Immutable Audit Trail)

CREATE TABLE reconciliation_logs (
    id UUID PRIMARY KEY,
    trade_id UUID NOT NULL REFERENCES trades(id),
    holding_id UUID NOT NULL REFERENCES holdings(id),
    quantity_before INT,
    quantity_after INT,
    cost_basis_delta DECIMAL(18,2),
    unrealized_gain_loss_delta DECIMAL(18,2),
    mismatch_detected BOOLEAN DEFAULT FALSE,
    mismatch_reason TEXT,  -- e.g., "quantity_variance", "price_variance", "settlement_delay"
    reconciled_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    correlation_id UUID NOT NULL
);

CREATE INDEX idx_reconciliation_logs_trade_id ON reconciliation_logs(trade_id);
CREATE INDEX idx_reconciliation_logs_holding_id ON reconciliation_logs(holding_id);
CREATE INDEX idx_reconciliation_logs_mismatch ON reconciliation_logs(mismatch_detected);

🔄 Cost Basis Calculation

Weighted Average Method

New Weighted Avg Cost = 
    (Previous Cost Basis + New Purchase Cost) / Total Quantity

Gain/Loss = Market Value - Total Cost Basis
Unrealized = Market Value - Cost Basis (for open positions)
Realized = (Execution Price - Avg Cost) × Quantity Sold

FIFO/LIFO Tracking (Lot Level)

CREATE TABLE lots (
    id UUID PRIMARY KEY,
    holding_id UUID REFERENCES holdings(id),
    purchase_date DATE,
    quantity INT,
    unit_cost DECIMAL(15,2),
    total_cost DECIMAL(18,2),
    status VARCHAR(50),  -- OPEN, PARTIAL_SOLD, CLOSED
    fifo_order INT,  -- For FIFO sequencing
    published_at TIMESTAMPTZ,
    correlation_id UUID
);

🔐 Mismatch Detection Rules

Type Condition Alert Level
Quantity Executed ≠ Approved (>0.1%) HIGH
Price Settlement > Limit (>2%) MEDIUM
Timing Settlement delay >2 days LOW
Settlement Unconfirmed >3 days HIGH
Cost Basis Recalc differs from ledger (>$0.01) MEDIUM

📋 API Contract

GET /reconciliation/holdings (Current Portfolio)

Query Params: security_id=uuid, include_mismatch=bool
Response (200):

{
  "items": [
    {
      "id": "holding-uuid",
      "securityId": "security-uuid",
      "quantity": 100,
      "weightedAvgCost": 150.50,
      "totalCostBasis": 15050.00,
      "marketValue": 18750.00,
      "unrealizedGainLoss": 3700.00,
      "updatedAt": "2026-09-10T14:30:00Z",
      "correlationId": "correlation-uuid"
    }
  ],
  "total": 1,
  "pages": 1
}

GET /reconciliation/mismatches (Flagged Discrepancies)

Query Params: severity=HIGH|MEDIUM|LOW, dateFrom=2026-09-01, dateTo=2026-09-30
Response (200):

{
  "items": [
    {
      "id": "log-uuid",
      "tradeId": "trade-uuid",
      "mismatchReason": "quantity_variance",
      "quantity": {"before": 100, "after": 99},
      "costBasisDelta": -150.50,
      "detectedAt": "2026-09-10T14:30:00Z"
    }
  ],
  "total": 2,
  "pages": 1
}

Governance Gates

Pre-Merge Gates

  • Schema: 3NF normalized, PIT tracked (published_at + correlation_id)
  • Calculation: Weighted avg cost, FIFO/LIFO lot tracking tested
  • Mismatch: Detection rules defined + prioritized
  • Immutability: reconciliation_logs INSERT-only, no UPDATE/DELETE
  • Audit: All state changes logged with CorrelationId

Post-Merge Validation (Deferred)

  • Integration tests (E2E trade → holdings update)
  • Cost basis calculation verified vs. accounting standards
  • Mismatch alert accuracy (low false-positive rate)
  • Performance: reconciliation completes <5 seconds

🛡️ Security & Compliance

Immutability Guarantees:

  • INSERT-only reconciliation_logs (no UPDATE, no DELETE)
  • Timestamp immutable after insertion
  • Correlation_id immutable (traceability)

Regulatory Requirements:

  • Cost basis accuracy (audited annually)
  • Lot tracking (tax reporting compliance)
  • Mismatch documentation (compliance review)

Access Control:

  • Portfolio Manager: Read/reconcile holdings
  • Finance: Read cost basis + gain/loss
  • Compliance: Read mismatch alerts + audit trail
  • System: Automatic reconciliation (no manual entry)

  • VS-03: Approval workflow (approval_proposals, evidence linkage)
  • VS-04: Audit trail (reconciliation events logged)
  • K (VS-12): Trade execution (provides trade_id, quantity, price)
  • VS-00: PIT envelope (published_at, correlation_id, revision)

Co-Authored-By: Claude Haiku 4.5 noreply@anthropic.com
Status: READY FOR IMPLEMENTATION
Next: Implement reconciliation engine (handlers, calculators, endpoints)