feat: add normalized PostgreSQL learning history
This commit is contained in:
@@ -176,7 +176,9 @@ quant_feed_contract:
|
||||
database_first_operating_model:
|
||||
purpose: "운영 이력, 원천 팩터, 파생 최종 팩터, 시장-결과 괴리를 PostgreSQL에 누적해 엔진을 고도화한다."
|
||||
canonical_store:
|
||||
primary: "PostgreSQL"
|
||||
primary: "PostgreSQL"
|
||||
canonical_engine_history_migration: "src/dotnet/QuantEngine.Infrastructure/Migrations/V5__Add_Normalized_Learning_History.sql"
|
||||
learning_projection: "engine_history.training_example_v1"
|
||||
secondary: "SQLite transient cache only"
|
||||
prohibited_operating_path:
|
||||
- "Excel workbook as operational source"
|
||||
@@ -190,6 +192,8 @@ quant_feed_contract:
|
||||
policy:
|
||||
- "최종 팩터와 최종 판단은 DB 이력 테이블에 버전과 시각을 함께 남긴다."
|
||||
- "시장 raw와 엔진 결과의 괴리는 별도 gap history로 적재한다."
|
||||
- "원천 관측·factor·decision·outcome은 정규화된 PostgreSQL event store에 적재한다."
|
||||
- "학습/캘리브레이션 입력은 training_example_v1 역정규화 view에서만 생성한다."
|
||||
- "엑셀/시트/Apps Script는 더 이상 운영 경로가 아니라, 역사적 import/export 또는 폐기 대상만 허용한다."
|
||||
- "새 분석·리포트는 PostgreSQL snapshot을 1차 진실원천으로 사용한다."
|
||||
xlsx_analysis_protocol:
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
-- V5__Add_Normalized_Learning_History.sql
|
||||
-- Normalized PostgreSQL event store for factor decisions and learning data.
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
CREATE SCHEMA IF NOT EXISTS engine_history;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS engine_history.source_observation (
|
||||
observation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
observed_at TIMESTAMPTZ NOT NULL,
|
||||
instrument_id TEXT NOT NULL,
|
||||
source_name TEXT NOT NULL,
|
||||
source_version TEXT NOT NULL,
|
||||
payload JSONB NOT NULL,
|
||||
provenance JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_source_observation_instrument_time
|
||||
ON engine_history.source_observation (instrument_id, observed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS engine_history.factor_definition (
|
||||
factor_id TEXT NOT NULL,
|
||||
factor_version TEXT NOT NULL,
|
||||
formula_id TEXT NOT NULL,
|
||||
effective_from TIMESTAMPTZ NOT NULL,
|
||||
effective_to TIMESTAMPTZ,
|
||||
definition JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
provenance JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
PRIMARY KEY (factor_id, factor_version)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS engine_history.factor_observation (
|
||||
factor_observation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
observation_id UUID NOT NULL REFERENCES engine_history.source_observation(observation_id),
|
||||
factor_id TEXT NOT NULL,
|
||||
factor_version TEXT NOT NULL,
|
||||
observed_at TIMESTAMPTZ NOT NULL,
|
||||
numeric_value NUMERIC,
|
||||
text_value TEXT,
|
||||
gate TEXT NOT NULL,
|
||||
provenance JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
CONSTRAINT fk_factor_definition
|
||||
FOREIGN KEY (factor_id, factor_version)
|
||||
REFERENCES engine_history.factor_definition(factor_id, factor_version),
|
||||
CONSTRAINT factor_value_present CHECK (numeric_value IS NOT NULL OR text_value IS NOT NULL)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_factor_observation_factor_time
|
||||
ON engine_history.factor_observation (factor_id, observed_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS engine_history.decision_event (
|
||||
decision_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
decision_key TEXT NOT NULL UNIQUE,
|
||||
decided_at TIMESTAMPTZ NOT NULL,
|
||||
instrument_id TEXT NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
gate TEXT NOT NULL,
|
||||
score NUMERIC,
|
||||
source_version TEXT NOT NULL,
|
||||
trace JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
provenance JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS ix_decision_event_instrument_time
|
||||
ON engine_history.decision_event (instrument_id, decided_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS engine_history.decision_factor_evidence (
|
||||
decision_id UUID NOT NULL REFERENCES engine_history.decision_event(decision_id),
|
||||
factor_observation_id UUID NOT NULL REFERENCES engine_history.factor_observation(factor_observation_id),
|
||||
role TEXT NOT NULL,
|
||||
PRIMARY KEY (decision_id, factor_observation_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS engine_history.outcome_evaluation (
|
||||
evaluation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
decision_id UUID NOT NULL REFERENCES engine_history.decision_event(decision_id),
|
||||
horizon_days INTEGER NOT NULL CHECK (horizon_days > 0),
|
||||
evaluated_at TIMESTAMPTZ NOT NULL,
|
||||
realized_return NUMERIC,
|
||||
benchmark_return NUMERIC,
|
||||
excess_return NUMERIC,
|
||||
outcome_class TEXT NOT NULL,
|
||||
evaluation_gate TEXT NOT NULL,
|
||||
provenance JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
UNIQUE (decision_id, horizon_days)
|
||||
);
|
||||
|
||||
-- Read-optimized projection for model training and calibration jobs.
|
||||
CREATE OR REPLACE VIEW engine_history.training_example_v1 AS
|
||||
SELECT
|
||||
d.decision_id,
|
||||
d.decision_key,
|
||||
d.decided_at,
|
||||
d.instrument_id,
|
||||
d.action,
|
||||
d.gate AS decision_gate,
|
||||
d.score,
|
||||
d.source_version,
|
||||
e.horizon_days,
|
||||
e.realized_return,
|
||||
e.benchmark_return,
|
||||
e.excess_return,
|
||||
e.outcome_class,
|
||||
e.evaluation_gate,
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'factor_id', f.factor_id,
|
||||
'factor_version', f.factor_version,
|
||||
'numeric_value', f.numeric_value,
|
||||
'text_value', f.text_value,
|
||||
'gate', f.gate,
|
||||
'role', evidence.role
|
||||
) ORDER BY f.factor_id) AS factor_features
|
||||
FROM engine_history.decision_event d
|
||||
JOIN engine_history.outcome_evaluation e ON e.decision_id = d.decision_id
|
||||
JOIN engine_history.decision_factor_evidence evidence ON evidence.decision_id = d.decision_id
|
||||
JOIN engine_history.factor_observation f ON f.factor_observation_id = evidence.factor_observation_id
|
||||
GROUP BY d.decision_id, d.decision_key, d.decided_at, d.instrument_id, d.action,
|
||||
d.gate, d.score, d.source_version, e.horizon_days, e.realized_return,
|
||||
e.benchmark_return, e.excess_return, e.outcome_class, e.evaluation_gate;
|
||||
Reference in New Issue
Block a user