From 74a83f94fb8608bf048b740745c9cf703c7c37f9 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Fri, 26 Jun 2026 18:07:02 +0900 Subject: [PATCH] ui dashboard cleanup --- .../Components/Layout/NavMenu.razor | 26 +- .../Components/Pages/Counter.razor | 19 - .../Components/Pages/Dashboard.razor | 326 +++++------------- .../Components/Pages/Operations.razor | 138 ++++++++ .../Components/Pages/Weather.razor | 64 ---- 5 files changed, 218 insertions(+), 355 deletions(-) delete mode 100644 src/dotnet/QuantEngine.Web/Components/Pages/Counter.razor create mode 100644 src/dotnet/QuantEngine.Web/Components/Pages/Operations.razor delete mode 100644 src/dotnet/QuantEngine.Web/Components/Pages/Weather.razor diff --git a/src/dotnet/QuantEngine.Web/Components/Layout/NavMenu.razor b/src/dotnet/QuantEngine.Web/Components/Layout/NavMenu.razor index 1debd68..50a2195 100644 --- a/src/dotnet/QuantEngine.Web/Components/Layout/NavMenu.razor +++ b/src/dotnet/QuantEngine.Web/Components/Layout/NavMenu.razor @@ -1,28 +1,8 @@ - + Dashboard - - - Portfolio - - - - Analytics - - - - Reports - - - - - - Settings - - - - Help + + Operations - diff --git a/src/dotnet/QuantEngine.Web/Components/Pages/Counter.razor b/src/dotnet/QuantEngine.Web/Components/Pages/Counter.razor deleted file mode 100644 index 1a4f8e7..0000000 --- a/src/dotnet/QuantEngine.Web/Components/Pages/Counter.razor +++ /dev/null @@ -1,19 +0,0 @@ -@page "/counter" -@rendermode InteractiveServer - -Counter - -

Counter

- -

Current count: @currentCount

- - - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor b/src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor index 08273f9..23893fe 100644 --- a/src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor +++ b/src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor @@ -1,310 +1,138 @@ @page "/" -@using QuantEngine.Core.Models -@using QuantEngine.Core.Interfaces -@inject NavigationManager NavManager -@inject ISnackbar Snackbar -@inject IPostgresqlHistorySnapshotReader HistoryReader +@using QuantEngine.Core.Infrastructure +@inject IWebHostEnvironment Environment Quant Engine - Dashboard -Quant Engine Dashboard +Quant Engine + + 루트 화면은 운영 진입점입니다. 가짜 성과 수치 없이 현재 스냅샷 상태와 리포트 경로만 보여줍니다. + - - + - Active Positions - @activePositions - from history snapshot + Operational Report + @ReportStateLabel + @ReportPath - - + - Portfolio Value - @portfolioValueLabel - PostgreSQL snapshot + Sections + @SectionCountLabel + Temp/operational_report.json - - + - Signal Quality - @signalQualityLabel - decision_result_history - - - - - - - - System Status - @dbStatusLabel + Primary Route + + Open Operations + - - - + + - Market Status + Current State - - Market Regime: @marketRegimeLabel - - - Volatility: @volatilityLabel - - - Cash Position: @cashPositionLabel - - - Last Updated: @lastUpdatedLabel - + Status: @ReportChipLabel + Generated: @GeneratedAtLabel + Source: @SourceLabel + Decision feed: @DecisionFeedLabel + Factor feed: @FactorFeedLabel + Raw feed: @RawFeedLabel - - + + - System Health + Routing Notes - - Database: - @databaseLabel - - - DB History Feed: - @historyFeedLabel - - - Signal Generator: - @signalGeneratorLabel - - - API Uptime: 99.8% - + - 운영 데이터는 snapshot 우선입니다. + - Excel/GAS 의존 문구는 운영 경로에서 제거 대상입니다. + - 숫자는 provenance 없으면 표시하지 않습니다. - - - - - Performance Metrics - - - - - - - YTD Return - @ytdReturnLabel - - - Sharpe Ratio - @sharpeLabel - - - Max Drawdown - @maxDrawdownLabel - - - - - - Win Rate - @winRateLabel - - - Profit Factor - @profitFactorLabel - - - Trades This Month - @tradesThisMonthLabel - - - - - - - - - - - Algorithm Status (v9 Hardening) - - - - - - Phase - Name - Status - Progress - - - @context["Phase"] - @context["Name"] - - @{ - var status = context["Status"].ToString(); - var chipColor = "Calibrated".Equals(status) ? Color.Success : Color.Info; - } - @status - - - @{ - var progress = context.TryGetValue("Progress", out var p) ? p?.ToString() ?? string.Empty : string.Empty; - var progressValue = int.TryParse(progress.Replace("%", ""), out var val) ? val : 0; - } - - - - - - - - - Recent Signals (Live Feed) + Coverage Summary - - - Timestamp - Ticker - Signal - Score - Style - Status - - - @context["Timestamp"] - @context["Ticker"] - - @{ - var signal = context["Signal"].ToString(); - var signalColor = "BUY".Equals(signal) ? Color.Success : Color.Warning; - } - @signal - - @context["Score"] - @context["Style"] - - @context["Status"] - - - + @if (Sections.Count == 0) + { + + DATA_MISSING: operational_report.json이 비어 있거나 아직 생성되지 않았습니다. + + } + else + { + + + Name + Title + Preview + + + @context.Name + @context.Title + @context.Preview + + + } @code { - private List> algorithmPhases = new(); - private List> recentSignals = new(); - private string activePositions = "0"; - private string portfolioValueLabel = "n/a"; - private string signalQualityLabel = "n/a"; - private string dbStatusLabel = "Pending"; - private string marketRegimeLabel = "PENDING"; - private string volatilityLabel = "n/a"; - private string cashPositionLabel = "n/a"; - private string lastUpdatedLabel = "n/a"; - private string databaseLabel = "Pending"; - private string historyFeedLabel = "Pending"; - private string signalGeneratorLabel = "Pending"; - private string ytdReturnLabel = "n/a"; - private string sharpeLabel = "n/a"; - private string maxDrawdownLabel = "n/a"; - private string winRateLabel = "n/a"; - private string profitFactorLabel = "n/a"; - private string tradesThisMonthLabel = "0"; + private readonly List Sections = new(); + private string ReportStateLabel = "DATA_MISSING"; + private string ReportChipLabel = "DATA_MISSING"; + private Color ReportChipColor = Color.Warning; + private string SectionCountLabel = "0"; + private string GeneratedAtLabel = "n/a"; + private string SourceLabel = "n/a"; + private string DecisionFeedLabel = "DISCONNECTED"; + private string FactorFeedLabel = "DISCONNECTED"; + private string RawFeedLabel = "DISCONNECTED"; + private string ReportPath = "n/a"; - protected override async Task OnInitializedAsync() + protected override void OnInitialized() { - await LoadHistoryAsync(); - } - - private async Task LoadHistoryAsync() - { - try - { - var decisions = await HistoryReader.ReadAsync("decision_result_history", 5); - activePositions = decisions.Count.ToString(); - signalQualityLabel = decisions.Count > 0 ? "snapshot" : "n/a"; - dbStatusLabel = decisions.Count > 0 ? "Connected" : "Empty"; - databaseLabel = dbStatusLabel; - historyFeedLabel = decisions.Count > 0 ? "Active" : "Pending"; - signalGeneratorLabel = decisions.Count > 0 ? "Snapshot-driven" : "Pending"; - marketRegimeLabel = decisions.Count > 0 ? "SNAPSHOT" : "PENDING"; - volatilityLabel = decisions.Count > 0 ? "Snapshot-derived" : "n/a"; - cashPositionLabel = decisions.Count > 0 ? "Snapshot-derived" : "n/a"; - lastUpdatedLabel = decisions.Count > 0 - ? (decisions[0].TryGetValue("decided_at", out var decidedAt) ? decidedAt?.ToString() ?? "n/a" : "n/a") - : "n/a"; - ytdReturnLabel = decisions.Count > 0 ? "snapshot" : "n/a"; - sharpeLabel = decisions.Count > 0 ? "snapshot" : "n/a"; - maxDrawdownLabel = decisions.Count > 0 ? "snapshot" : "n/a"; - winRateLabel = decisions.Count > 0 ? "snapshot" : "n/a"; - profitFactorLabel = decisions.Count > 0 ? "snapshot" : "n/a"; - tradesThisMonthLabel = decisions.Count.ToString(); - recentSignals = decisions.Select(row => new Dictionary - { - { "Timestamp", row.TryGetValue("decided_at", out var decidedAt) ? decidedAt?.ToString() ?? "" : "" }, - { "Ticker", row.TryGetValue("instrument_id", out var ticker) ? ticker?.ToString() ?? "" : "" }, - { "Signal", row.TryGetValue("action", out var action) ? action?.ToString() ?? "" : "" }, - { "Score", row.TryGetValue("score", out var score) ? score?.ToString() ?? "" : "" }, - { "Style", row.TryGetValue("source_version", out var sourceVersion) ? sourceVersion?.ToString() ?? "" : "" }, - { "Status", row.TryGetValue("gate", out var gate) ? gate?.ToString() ?? "" : "" } - }).ToList(); - - var rawCount = (await HistoryReader.ReadAsync("market_raw_history", 1)).Count; - var factorCount = (await HistoryReader.ReadAsync("factor_output_history", 1)).Count; - var gapCount = (await HistoryReader.ReadAsync("market_vs_engine_gap_history", 1)).Count; - portfolioValueLabel = rawCount > 0 ? "snapshot" : "n/a"; - - algorithmPhases = new() - { - new() { { "Phase", "P0" }, { "Name", "History Contract" }, { "Status", "Calibrated" }, { "Progress", "100%" } }, - new() { { "Phase", "P1" }, { "Name", "PostgreSQL Store" }, { "Status", rawCount > 0 ? "Active" : "Pending" }, { "Progress", rawCount > 0 ? "100%" : "0%" } }, - new() { { "Phase", "P2" }, { "Name", "Factor Output History" }, { "Status", factorCount > 0 ? "Active" : "Pending" }, { "Progress", factorCount > 0 ? "100%" : "0%" } }, - new() { { "Phase", "P3" }, { "Name", "Decision Result History" }, { "Status", recentSignals.Count > 0 ? "Active" : "Pending" }, { "Progress", recentSignals.Count > 0 ? "100%" : "0%" } }, - new() { { "Phase", "P4" }, { "Name", "Gap History" }, { "Status", gapCount > 0 ? "Active" : "Pending" }, { "Progress", gapCount > 0 ? "100%" : "0%" } } - }; - } - catch - { - algorithmPhases = new() - { - new() { { "Phase", "P0" }, { "Name", "History Contract" }, { "Status", "Pending" }, { "Progress", "0%" } } - }; - recentSignals = new(); - } + ReportPath = Path.GetFullPath(Path.Combine(Environment.ContentRootPath, "..", "..", "..", "Temp", "operational_report.json")); + var report = OperationalReportLoader.Load(ReportPath); + Sections.AddRange(report.Sections); + SectionCountLabel = report.SectionCount.ToString(); + GeneratedAtLabel = report.GeneratedAt; + SourceLabel = report.SourceJson; + ReportStateLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING"; + ReportChipLabel = Sections.Count > 0 ? "READY" : "DATA_MISSING"; + ReportChipColor = Sections.Count > 0 ? Color.Success : Color.Warning; } } diff --git a/src/dotnet/QuantEngine.Web/Components/Pages/Operations.razor b/src/dotnet/QuantEngine.Web/Components/Pages/Operations.razor new file mode 100644 index 0000000..ace83da --- /dev/null +++ b/src/dotnet/QuantEngine.Web/Components/Pages/Operations.razor @@ -0,0 +1,138 @@ +@page "/operations" +@using QuantEngine.Core.Infrastructure +@inject IWebHostEnvironment Environment + +Quant Engine - Operations + +Operational Report + + 이 페이지는 `Temp/operational_report.json`만 읽습니다. DB 연결과 무관하게 동일한 결과를 보여주는 운영 고정 화면입니다. + + + + + + + Schema + @SchemaVersion + + + + + + + Sections + @SectionCountLabel + + + + + + + Source + @SourceJson + + + + + + + Generated + @GeneratedAt + + + + + + + @foreach (var section in HighlightSections) + { + + + + @(section.Name) + @(section.Title) + @(section.Preview) + + + + } + + + + + + Report Health + + + + + Status: @HealthLabel + Path: @ReportPath + Sections rendered: @RenderedSectionCountLabel + + + + + + + + Sections + + + + @if (Sections.Count == 0) + { + + DATA_MISSING: operational_report.json에 표시할 섹션이 없습니다. + + } + else + { + + + Name + Title + Preview + + + @context.Name + @context.Title + @context.Preview + + + } + + + +@code { + private readonly List Sections = new(); + private readonly List HighlightSections = new(); + private string SchemaVersion = "n/a"; + private string SourceJson = "n/a"; + private string GeneratedAt = "n/a"; + private string SectionCountLabel = "0"; + private string RenderedSectionCountLabel = "0"; + private string HealthLabel = "DATA_MISSING"; + private Color HealthColor = Color.Warning; + private string ReportPath = "n/a"; + + protected override async Task OnInitializedAsync() + { + ReportPath = Path.GetFullPath(Path.Combine(Environment.ContentRootPath, "..", "..", "..", "Temp", "operational_report.json")); + + var report = OperationalReportLoader.Load(ReportPath); + SchemaVersion = report.SchemaVersion; + SourceJson = report.SourceJson; + GeneratedAt = report.GeneratedAt; + Sections.AddRange(report.Sections); + + HighlightSections.Clear(); + HighlightSections.AddRange(Sections.Take(4)); + + SectionCountLabel = report.SectionCount.ToString(); + RenderedSectionCountLabel = Sections.Count.ToString(); + HealthLabel = Sections.Count > 0 ? "PASS" : "DATA_MISSING"; + HealthColor = Sections.Count > 0 ? Color.Success : Color.Warning; + } +} diff --git a/src/dotnet/QuantEngine.Web/Components/Pages/Weather.razor b/src/dotnet/QuantEngine.Web/Components/Pages/Weather.razor deleted file mode 100644 index f437e5e..0000000 --- a/src/dotnet/QuantEngine.Web/Components/Pages/Weather.razor +++ /dev/null @@ -1,64 +0,0 @@ -@page "/weather" -@attribute [StreamRendering] - -Weather - -

Weather

- -

This component demonstrates showing data.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - // Simulate asynchronous loading to demonstrate streaming rendering - await Task.Delay(500); - - var startDate = DateOnly.FromDateTime(DateTime.Now); - var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = summaries[Random.Shared.Next(summaries.Length)] - }).ToArray(); - } - - private class WeatherForecast - { - public DateOnly Date { get; set; } - public int TemperatureC { get; set; } - public string? Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } -}