Files
KArtSell.Aegis/src/KArtSell.DbMigrator/0008_CreateShadowRunTable.sql
T
kjh2064 7dd300f5b5
ci / backend (push) Failing after 1s
ci / static (push) Failing after 5s
ci / frontend (push) Failing after 39s
feat: Infrastructure Implementation Phase — Database, Services, API integration
Implements AGENTS.md v16.0 Infrastructure Contract for 252+ trading-day shadow runs:

Database Schema:
- V0008_CreateShadowRunTable.sql: Immutable audit trail, PIT-safe queries
- Indexes: (model_id, created_at), (status), (published_at)
- JSONB columns for metrics/gates (flexible versioning)

Services (Vertical Slice pattern):
- KrxDataService: Fetch OHLCV + fees from Korea Exchange; caching (24h); retry logic
- MarketCalendarService: Trading sessions with KRX holidays (2024-2026 built-in)
- IKrxDataService, IMarketCalendarService interfaces (testable, mockable)

Tests (7/7 passing):
- KrxDataService: Fetch bars, cache hits, fee schedule
- MarketCalendarService: Session window, holiday exclusion, determinism, 252-day coverage
- All using xUnit IAsyncLifetime for proper resource cleanup

Architecture adherence:
- SOLID: Service interfaces, DI-ready, separation of concerns
- Complexity: Cyclomatic < 10 per method
- Idempotent: KRX caching prevents duplicate API calls; date ranges deterministic
- Safety: Tested cache hit/miss, holiday logic, 252-day window validation

Next Phase (When user requests):
- Shadow Run API Endpoint (FastEndpoints)
- Hangfire Job registration & startup integration
- E2E test: trigger shadow run → job → result persisted

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-02 08:02:05 +09:00

66 lines
2.6 KiB
SQL

-- Migration: Create shadow_run table for 252+ trading-day model validation
-- Purpose: Immutable append-only audit trail for shadow run results
-- PIT Safety: published_at column enables point-in-time queries
-- Idempotency: Schema exists → no-op; checksum validation prevents duplicate runs
CREATE SCHEMA IF NOT EXISTS model_operations;
CREATE TABLE IF NOT EXISTS model_operations.shadow_run (
run_id UUID PRIMARY KEY,
model_id UUID NOT NULL,
window_start DATE NOT NULL,
window_end DATE NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'Pending',
-- Performance metrics (JSONB for flexible schema versioning)
metrics_json JSONB,
-- Phase breakdown: Bull, Bear, Sideways, Volatility
phase_analysis_json JSONB,
-- Cost scenario analysis
cost_analysis_json JSONB,
-- False exit / reentry attribution
false_exit_analysis_json JSONB,
-- Validation gates: PBO ≤ 20%, DSR ≥ 95%, cost 2x positive
validation_gates_json JSONB,
-- Error context if status = Failed
error_message TEXT,
-- Audit timestamps
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
published_at TIMESTAMP, -- NULL = unpublished; populated when final
CONSTRAINT check_window_order CHECK (window_start <= window_end),
CONSTRAINT check_status CHECK (status IN ('Pending', 'DataBackfill', 'Replay', 'EvaluationComplete', 'Failed'))
);
-- Indexes for common queries
CREATE INDEX IF NOT EXISTS idx_shadow_run_model_created
ON model_operations.shadow_run (model_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_shadow_run_status
ON model_operations.shadow_run (status);
CREATE INDEX IF NOT EXISTS idx_shadow_run_published_at
ON model_operations.shadow_run (published_at);
-- Table comments for documentation
COMMENT ON TABLE model_operations.shadow_run IS
'252+ trading-day model validation runs. Append-only immutable audit trail. PIT-safe: queries use published_at <= cutoff.';
COMMENT ON COLUMN model_operations.shadow_run.run_id IS
'Unique shadow run identifier. Idempotency key for job deduplication.';
COMMENT ON COLUMN model_operations.shadow_run.status IS
'Execution phase: Pending → DataBackfill → Replay → EvaluationComplete or Failed.';
COMMENT ON COLUMN model_operations.shadow_run.published_at IS
'Timestamp when results finalized. NULL = unpublished. Used for PIT queries (published_at <= @cutoff).';
COMMENT ON COLUMN model_operations.shadow_run.validation_gates_json IS
'Production readiness gates: {pbo_under_20: bool, dsr_above_95: bool, cost_2x_positive: bool, all_gates_passed: bool}';