Files
KArtSell.Aegis/docs/CURRENT/SLICE_SPECS/VS-29-SLICE_SPEC.md
T
kjh2064 cedc8d79ee docs: resolve VS-03/04/12/14 numbering collision (renumber to VS-26/27/28/29)
Renumbers the four 2026-08-07 slices (ApprovalWorkflow, AuditTrail,
TradeExecution, PortfolioReconciliation) to previously-unused VS-26..29,
leaving WBS_MASTER.csv's original VS-03/04/12/14 definitions (IngestMarketDataPIT,
ApplyCorporateActions, RankBuyCandidates, GenerateDailyRecommendations) untouched,
per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md.

While investigating, found two things not yet resolved by this commit:
- DEBT-017 (duplicate ApprovalWorkflow implementation): the tested backend
  (ApprovalWorkflow/) is [DontRegister]'d dead code; the live one
  (Features/ApprovalWorkflow/, wired in Program.cs) has no dedicated tests.
  AEG-VS-26-01 downgraded from COMPLETED to BLOCKED in the tracker pending an
  architect decision on which implementation is canonical.
- Features/MarketData and Features/Portfolio (VS-03/04/05/08 Market Data
  Ingestion Dashboard, Portfolio Rebalance, Risk Metrics, Dashboard) are a
  third, already-implemented-and-tested body of work entirely absent from
  WBS_PROGRESS_TRACKER.csv. Flagged in CURRENT_ROADMAP.md as a follow-up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-08 12:36:00 +09:00

7.2 KiB
Raw Blame History

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

Vertical Slice: VS-29 (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-26 (approval) + VS-27 (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-28)
  ↓
Extract trade details: quantity, price, settlement date
  ↓
Validate against approval (from VS-26)
  ↓
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-26: Approval workflow (approval_proposals, evidence linkage)
  • VS-27: Audit trail (reconciliation events logged)
  • K (VS-28): 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)