Supervisor S-1: Dossier-Generator + Modul-Skelett mit Dossier-Browser + Journal-Nachverdrahtung
Neues Modul PolyTrader.Modules.Supervisor (IPolyTraderModule, Name=Supervisor, DbPrefix=sup_, nur Core-Referenz, strikt read-only): - DossierBuilder (Core/Analytics, pur+getestet): TradeDossier aus Entscheidungen + Order-Events + Trades + Log-Zeilen, chronologisch, mit Markdown-Rendering (Tabellen, Pipe-Escaping). - DossierService (Modul): beschafft Journal/Events/Trade-Log per SignalId + JSONL-Zeilen per CID (nur Tagesdateien im Ereignis-Zeitfenster +-1 Tag); RecentSignals-Uebersicht (Journal gruppiert). - SupervisorMainForm: Dossier-Browser - links juengste Signale, rechts Markdown-Dossier; SignalId-Suche; Analyse-Chat (OpenRouter) folgt in S-2. In Launcher/Smoke registriert. Journal-Nachverdrahtung (S-0-Vervollstaendigung): - TraderMonitor: Profit-Target erzeugt eigene SignalId -> Leiter + Journal (ProfitTargetTriggered); Stale-Cleanup-Cancels als OrderEvents (StaleCleanupCancel). - StartupOrderReconciliation: K2-Cancels als OrderEvents (StartupReconcileCancel). - RF: Demo-Einstiege (DemoFilled, eigene SignalId) + Resolution-Closes (SystemResolutionClose) im Journal - damit sind ALLE Module im Entscheidungsjournal vertreten. Tests: +3 DossierBuilder; 4 Service-Builder auf neue Ctors. Build 0 Fehler, 344 Tests gruen, --smoke-ui: [OK] supervisor.main (alle 5 Views gruen). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ca00977ea3
commit
c75a958e36
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PolyTrader.Core.Analytics;
|
||||
using PolyTrader.Modules.Supervisor.Services;
|
||||
|
||||
namespace PolyTrader.Modules.Supervisor.Ui
|
||||
{
|
||||
/// <summary>
|
||||
/// Hauptfenster des Supervisor-Moduls (S-1): Dossier-Browser — links die jüngsten Signale
|
||||
/// (aus dem Entscheidungsjournal), rechts das komplette Dossier (Markdown). Der Analyse-Chat
|
||||
/// (OpenRouter-Agent) folgt in S-2 als weiterer Tab. Code-only konstruiert (Muster RF-Modul);
|
||||
/// DB-Zugriffe defensiv, damit die UI auch bei leerer/nicht erreichbarer DB bedienbar bleibt.
|
||||
/// </summary>
|
||||
public sealed class SupervisorMainForm : Form
|
||||
{
|
||||
private DossierService? _dossiers;
|
||||
|
||||
private readonly ToolStrip _toolStrip = new();
|
||||
private readonly ToolStripButton _btnRefresh = new() { Text = "Aktualisieren", DisplayStyle = ToolStripItemDisplayStyle.Text };
|
||||
private readonly ToolStripLabel _lblSearch = new() { Text = "SignalId:" };
|
||||
private readonly ToolStripTextBox _tbSignalId = new() { AutoSize = false, Width = 220 };
|
||||
private readonly ToolStripButton _btnOpen = new() { Text = "Dossier öffnen", DisplayStyle = ToolStripItemDisplayStyle.Text };
|
||||
|
||||
private readonly SplitContainer _split = new() { Dock = DockStyle.Fill, SplitterDistance = 420 };
|
||||
private readonly DataGridView _grid = new()
|
||||
{
|
||||
Dock = DockStyle.Fill, ReadOnly = true, AllowUserToAddRows = false, AllowUserToDeleteRows = false,
|
||||
AutoGenerateColumns = true, SelectionMode = DataGridViewSelectionMode.FullRowSelect,
|
||||
RowHeadersVisible = false, MultiSelect = false
|
||||
};
|
||||
private readonly TextBox _dossierText = new()
|
||||
{
|
||||
Dock = DockStyle.Fill, Multiline = true, ReadOnly = true, ScrollBars = ScrollBars.Both,
|
||||
Font = new System.Drawing.Font("Consolas", 9.5f), WordWrap = false
|
||||
};
|
||||
private readonly Label _status = new() { Dock = DockStyle.Bottom, Height = 22, Padding = new Padding(6, 2, 6, 2), Text = "" };
|
||||
|
||||
public SupervisorMainForm()
|
||||
{
|
||||
Text = "Supervisor";
|
||||
Width = 1250;
|
||||
Height = 700;
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
|
||||
_toolStrip.Items.AddRange(new ToolStripItem[] { _btnRefresh, new ToolStripSeparator(), _lblSearch, _tbSignalId, _btnOpen });
|
||||
_split.Panel1.Controls.Add(_grid);
|
||||
_split.Panel2.Controls.Add(_dossierText);
|
||||
|
||||
Controls.Add(_split);
|
||||
Controls.Add(_toolStrip);
|
||||
Controls.Add(_status);
|
||||
_toolStrip.Dock = DockStyle.Top;
|
||||
|
||||
_btnRefresh.Click += (_, _) => LoadSignals();
|
||||
_btnOpen.Click += (_, _) => OpenDossier(_tbSignalId.Text.Trim());
|
||||
_grid.SelectionChanged += (_, _) => OpenSelected();
|
||||
}
|
||||
|
||||
public void Initialize(IServiceProvider services)
|
||||
{
|
||||
_dossiers = services.GetRequiredService<DossierService>();
|
||||
LoadSignals();
|
||||
}
|
||||
|
||||
private void LoadSignals()
|
||||
{
|
||||
if (_dossiers == null) return;
|
||||
try
|
||||
{
|
||||
List<SignalSummary> signals = _dossiers.RecentSignals(200);
|
||||
_grid.DataSource = signals;
|
||||
_status.Text = signals.Count == 0
|
||||
? "Noch keine Journal-Einträge (Entscheidungen entstehen, sobald Signale verarbeitet werden)."
|
||||
: $"{signals.Count} Signale.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_status.Text = $"Journal nicht lesbar: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenSelected()
|
||||
{
|
||||
if (_grid.CurrentRow?.DataBoundItem is SignalSummary s)
|
||||
OpenDossier(s.SignalId);
|
||||
}
|
||||
|
||||
private void OpenDossier(string signalId)
|
||||
{
|
||||
if (_dossiers == null || string.IsNullOrWhiteSpace(signalId)) return;
|
||||
try
|
||||
{
|
||||
var dossier = _dossiers.BuildForSignal(signalId);
|
||||
_dossierText.Text = DossierBuilder.ToMarkdown(dossier).Replace("\n", Environment.NewLine);
|
||||
_tbSignalId.Text = signalId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_dossierText.Text = $"Dossier konnte nicht geladen werden: {ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user