feat(quant): WBS-FE-BE-100 complete Vue3 Vite8 SPA & .NET10 FastEndpoints refactoring
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Failing after 13s
Validators (Pushes and Pull Requests) / validate-core (push) Failing after 19s

This commit is contained in:
2026-07-22 15:14:28 +09:00
parent fd8ff3d51e
commit 2fe4cb288f
100 changed files with 7975 additions and 804 deletions
@@ -0,0 +1,120 @@
-- V8__PostgreSQL_History_First_Schema.sql
-- PostgreSQL History-First Operating Model Canonical Database Schema
-- Standard: 3NF Relational Core + JSONB Provenance Payload + Audit Logging
-- 1. Create Schema if not exists
CREATE SCHEMA IF NOT EXISTS quantengine;
-- 2. Market Raw History Time-Series
CREATE TABLE IF NOT EXISTS quantengine.market_raw_history (
id BIGSERIAL PRIMARY KEY,
ticker VARCHAR(32) NOT NULL,
as_of_date VARCHAR(10) NOT NULL,
open_price NUMERIC(18, 4),
high_price NUMERIC(18, 4),
low_price NUMERIC(18, 4),
close_price NUMERIC(18, 4) NOT NULL,
volume BIGINT,
nav_price NUMERIC(18, 4),
disparate_ratio NUMERIC(10, 6),
tracking_error NUMERIC(10, 6),
aum_krw NUMERIC(20, 2),
raw_payload JSONB NOT NULL,
provenance JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT uk_market_raw_ticker_date UNIQUE (ticker, as_of_date)
);
CREATE INDEX IF NOT EXISTS idx_market_raw_ticker_date ON quantengine.market_raw_history (ticker, as_of_date DESC);
-- 3. Factor Version & Definition History
CREATE TABLE IF NOT EXISTS quantengine.factor_version_history (
factor_id VARCHAR(64) PRIMARY KEY,
formula_name VARCHAR(128) NOT NULL,
version VARCHAR(32) NOT NULL,
category VARCHAR(64) NOT NULL,
calibration_state VARCHAR(32) NOT NULL DEFAULT 'UNTESTED',
threshold_params JSONB NOT NULL,
description TEXT,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 4. Factor Output History (Calculation Store)
CREATE TABLE IF NOT EXISTS quantengine.factor_output_history (
id BIGSERIAL PRIMARY KEY,
run_id VARCHAR(64) NOT NULL,
ticker VARCHAR(32) NOT NULL,
as_of_date VARCHAR(10) NOT NULL,
factor_id VARCHAR(64) NOT NULL,
score NUMERIC(10, 4),
calculation_state VARCHAR(32) NOT NULL,
provenance JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT fk_factor_version FOREIGN KEY (factor_id) REFERENCES quantengine.factor_version_history (factor_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_factor_output_run_ticker ON quantengine.factor_output_history (run_id, ticker);
-- 5. Decision Result History (Signal & Risk Engine Output)
CREATE TABLE IF NOT EXISTS quantengine.decision_result_history (
id BIGSERIAL PRIMARY KEY,
run_id VARCHAR(64) NOT NULL UNIQUE,
as_of_date VARCHAR(10) NOT NULL,
market_regime VARCHAR(32) NOT NULL,
portfolio_health VARCHAR(32) NOT NULL,
rebalance_required BOOLEAN NOT NULL DEFAULT FALSE,
mid_check_required BOOLEAN NOT NULL DEFAULT FALSE,
total_asset_krw NUMERIC(20, 2) NOT NULL,
d2_cash_krw NUMERIC(20, 2) NOT NULL,
decision_packet_json JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- 6. Order Waterfall Execution History
CREATE TABLE IF NOT EXISTS quantengine.order_waterfall_execution_history (
id BIGSERIAL PRIMARY KEY,
run_id VARCHAR(64) NOT NULL,
ticker VARCHAR(32) NOT NULL,
sell_priority_rank INT NOT NULL,
waterfall_stage VARCHAR(64) NOT NULL,
action VARCHAR(16) NOT NULL,
target_qty INT NOT NULL,
executed_qty INT DEFAULT 0,
target_price NUMERIC(18, 4),
executed_price NUMERIC(18, 4),
bid_ask_spread_bps NUMERIC(10, 2),
slippage_bps NUMERIC(10, 2),
status VARCHAR(32) NOT NULL,
rationale TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT fk_decision_waterfall FOREIGN KEY (run_id) REFERENCES quantengine.decision_result_history (run_id) ON DELETE CASCADE
);
-- 7. Shadow Ledger History (Blocked/Gate Orders Audit)
CREATE TABLE IF NOT EXISTS quantengine.shadow_ledger_history (
id BIGSERIAL PRIMARY KEY,
run_id VARCHAR(64) NOT NULL,
ticker VARCHAR(32) NOT NULL,
blocked_gate VARCHAR(64) NOT NULL,
blocked_reason TEXT NOT NULL,
shadow_price NUMERIC(18, 4) NOT NULL,
shadow_qty INT NOT NULL,
shadow_tp_price NUMERIC(18, 4),
shadow_sl_price NUMERIC(18, 4),
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT fk_decision_shadow FOREIGN KEY (run_id) REFERENCES quantengine.decision_result_history (run_id) ON DELETE CASCADE
);
-- 8. Scheduler State Machine History
CREATE TABLE IF NOT EXISTS quantengine.scheduler_state_history (
id BIGSERIAL PRIMARY KEY,
task_name VARCHAR(64) NOT NULL,
execution_id VARCHAR(64) NOT NULL UNIQUE,
state VARCHAR(32) NOT NULL,
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
finished_at TIMESTAMPTZ,
error_message TEXT,
lock_token VARCHAR(64)
);
CREATE INDEX IF NOT EXISTS idx_scheduler_state_task ON quantengine.scheduler_state_history (task_name, state);
@@ -6,6 +6,10 @@ using QuantEngine.Core.Interfaces;
namespace QuantEngine.Infrastructure.Repositories
{
/// <summary>
/// PostgreSQL Dapper Repository implementation for History-First Operating Model.
/// Manages 3NF Data Integrity, Waterfall Auditing, and Shadow Ledger persistence.
/// </summary>
public class PostgresqlHistoryStore : IPostgresqlHistoryStore
{
private readonly IDbConnectionFactory _connectionFactory;
@@ -68,5 +72,35 @@ namespace QuantEngine.Infrastructure.Repositories
var rows = await conn.QueryAsync(sql, new { Limit = limit });
return rows.Select(row => (IDictionary<string, object?>)row).ToList();
}
public async Task<long> RecordWaterfallExecutionAsync(string runId, string ticker, int rank, string stage, string action, int targetQty, decimal? targetPrice, decimal? bidAskSpreadBps, decimal? slippageBps, string status, string rationale)
{
using var conn = _connectionFactory.CreateConnection();
conn.Open();
const string sql = @"
INSERT INTO quantengine.order_waterfall_execution_history
(run_id, ticker, sell_priority_rank, waterfall_stage, action, target_qty, target_price, bid_ask_spread_bps, slippage_bps, status, rationale)
VALUES
(@runId, @ticker, @rank, @stage, @action, @targetQty, @targetPrice, @bidAskSpreadBps, @slippageBps, @status, @rationale)
RETURNING id;";
return await conn.ExecuteScalarAsync<long>(sql, new { runId, ticker, rank, stage, action, targetQty, targetPrice, bidAskSpreadBps, slippageBps, status, rationale });
}
public async Task<long> RecordShadowLedgerAsync(string runId, string ticker, string blockedGate, string blockedReason, decimal shadowPrice, int shadowQty, decimal? shadowTpPrice, decimal? shadowSlPrice)
{
using var conn = _connectionFactory.CreateConnection();
conn.Open();
const string sql = @"
INSERT INTO quantengine.shadow_ledger_history
(run_id, ticker, blocked_gate, blocked_reason, shadow_price, shadow_qty, shadow_tp_price, shadow_sl_price)
VALUES
(@runId, @ticker, @blockedGate, @blockedReason, @shadowPrice, @shadowQty, @shadowTpPrice, @shadowSlPrice)
RETURNING id;";
return await conn.ExecuteScalarAsync<long>(sql, new { runId, ticker, blockedGate, blockedReason, shadowPrice, shadowQty, shadowTpPrice, shadowSlPrice });
}
}
}