diff --git a/PolyTrader.App.csproj b/PolyTrader.App.csproj
index 3159cd4..6afd692 100644
--- a/PolyTrader.App.csproj
+++ b/PolyTrader.App.csproj
@@ -70,10 +70,12 @@
+
+
diff --git a/Program.cs b/Program.cs
index 3c362b3..dd9dd1f 100644
--- a/Program.cs
+++ b/Program.cs
@@ -454,6 +454,19 @@ internal static class Program
Console.WriteLine($"[FEHLER] LauncherForm: {ex.GetType().Name}: {ex.Message}");
}
+ // Core-Dashboard direkt konstruieren (im Smoke nicht via uiHost registriert) - prueft das ScottPlot-Rendering headless.
+ try
+ {
+ using var dash = new PolyTraderSharp.Ui.Views.DashboardView();
+ dash.Initialize(host.Services.GetRequiredService(), host.Services.GetRequiredService());
+ Console.WriteLine("[OK] core.dashboard konstruiert (inkl. Charts)");
+ }
+ catch (Exception ex)
+ {
+ failures++;
+ Console.WriteLine($"[FEHLER] core.dashboard: {ex.GetType().Name}: {ex.Message}");
+ }
+
Console.WriteLine(failures == 0 ? "=== Smoke-UI OK ===" : $"=== Smoke-UI: {failures} Fehler ===");
return failures;
}
diff --git a/Ui/Views/DashboardView.Designer.cs b/Ui/Views/DashboardView.Designer.cs
index fdb11b1..a9cc1c6 100644
--- a/Ui/Views/DashboardView.Designer.cs
+++ b/Ui/Views/DashboardView.Designer.cs
@@ -17,9 +17,29 @@ namespace PolyTraderSharp.Ui.Views
private void InitializeComponent()
{
- this.pnlTop = new System.Windows.Forms.Panel();
- this.lblSummary = new System.Windows.Forms.Label();
- this.btnRefresh = new System.Windows.Forms.Button();
+ this.toolStripDash = new System.Windows.Forms.ToolStrip();
+ this.tslKonto = new System.Windows.Forms.ToolStripLabel();
+ this.cbAccount = new System.Windows.Forms.ToolStripComboBox();
+ this.tslModul = new System.Windows.Forms.ToolStripLabel();
+ this.cbModule = new System.Windows.Forms.ToolStripComboBox();
+ this.tslArt = new System.Windows.Forms.ToolStripLabel();
+ this.cbMode = new System.Windows.Forms.ToolStripComboBox();
+ this.tslZeit = new System.Windows.Forms.ToolStripLabel();
+ this.cbRange = new System.Windows.Forms.ToolStripComboBox();
+ this.tsRefresh = new System.Windows.Forms.ToolStripButton();
+ this.tabControlDash = new System.Windows.Forms.TabControl();
+ this.tabDashboard = new System.Windows.Forms.TabPage();
+ this.picEquity = new System.Windows.Forms.PictureBox();
+ this.pnlChartsBottom = new System.Windows.Forms.Panel();
+ this.picDay = new System.Windows.Forms.PictureBox();
+ this.picModule = new System.Windows.Forms.PictureBox();
+ this.flpKpis = new System.Windows.Forms.FlowLayoutPanel();
+ this.lblKpiPnl = new System.Windows.Forms.Label();
+ this.lblKpiWin = new System.Windows.Forms.Label();
+ this.lblKpiTrades = new System.Windows.Forms.Label();
+ this.lblKpiAvg = new System.Windows.Forms.Label();
+ this.lblKpiPf = new System.Windows.Forms.Label();
+ this.tabHistory = new System.Windows.Forms.TabPage();
this.dgvTrades = new System.Windows.Forms.DataGridView();
this.colModule = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colAccount = new System.Windows.Forms.DataGridViewTextBoxColumn();
@@ -32,37 +52,242 @@ namespace PolyTraderSharp.Ui.Views
this.colPnl = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colPnlPct = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colClosedAt = new System.Windows.Forms.DataGridViewTextBoxColumn();
- this.pnlTop.SuspendLayout();
+ this.pnlHistFilters = new System.Windows.Forms.Panel();
+ this.lblSuche = new System.Windows.Forms.Label();
+ this.tbSearch = new System.Windows.Forms.TextBox();
+ this.lblErgebnis = new System.Windows.Forms.Label();
+ this.cbWinLoss = new System.Windows.Forms.ComboBox();
+ this.toolStripDash.SuspendLayout();
+ this.tabControlDash.SuspendLayout();
+ this.tabDashboard.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.picEquity)).BeginInit();
+ this.pnlChartsBottom.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.picDay)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.picModule)).BeginInit();
+ this.flpKpis.SuspendLayout();
+ this.tabHistory.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).BeginInit();
+ this.pnlHistFilters.SuspendLayout();
this.SuspendLayout();
//
- // pnlTop
+ // toolStripDash
//
- this.pnlTop.Controls.Add(this.lblSummary);
- this.pnlTop.Controls.Add(this.btnRefresh);
- this.pnlTop.Dock = System.Windows.Forms.DockStyle.Top;
- this.pnlTop.Location = new System.Drawing.Point(0, 0);
- this.pnlTop.Name = "pnlTop";
- this.pnlTop.Size = new System.Drawing.Size(1100, 40);
- this.pnlTop.TabIndex = 0;
+ this.toolStripDash.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.tslKonto, this.cbAccount, this.tslModul, this.cbModule, this.tslArt, this.cbMode,
+ this.tslZeit, this.cbRange, this.tsRefresh});
+ this.toolStripDash.Location = new System.Drawing.Point(0, 0);
+ this.toolStripDash.Name = "toolStripDash";
+ this.toolStripDash.Size = new System.Drawing.Size(1200, 27);
+ this.toolStripDash.TabIndex = 0;
//
- // lblSummary
+ // tslKonto
//
- this.lblSummary.AutoSize = true;
- this.lblSummary.Location = new System.Drawing.Point(148, 12);
- this.lblSummary.Name = "lblSummary";
- this.lblSummary.Size = new System.Drawing.Size(63, 15);
- this.lblSummary.TabIndex = 1;
- this.lblSummary.Text = "—";
+ this.tslKonto.Name = "tslKonto";
+ this.tslKonto.Text = "Konto:";
//
- // btnRefresh
+ // cbAccount
//
- this.btnRefresh.Location = new System.Drawing.Point(8, 6);
- this.btnRefresh.Name = "btnRefresh";
- this.btnRefresh.Size = new System.Drawing.Size(130, 28);
- this.btnRefresh.TabIndex = 0;
- this.btnRefresh.Text = "Aktualisieren";
- this.btnRefresh.UseVisualStyleBackColor = true;
+ this.cbAccount.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbAccount.Name = "cbAccount";
+ this.cbAccount.Size = new System.Drawing.Size(170, 27);
+ //
+ // tslModul
+ //
+ this.tslModul.Name = "tslModul";
+ this.tslModul.Text = "Modul:";
+ //
+ // cbModule
+ //
+ this.cbModule.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbModule.Name = "cbModule";
+ this.cbModule.Size = new System.Drawing.Size(150, 27);
+ //
+ // tslArt
+ //
+ this.tslArt.Name = "tslArt";
+ this.tslArt.Text = "Art:";
+ //
+ // cbMode
+ //
+ this.cbMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbMode.Name = "cbMode";
+ this.cbMode.Size = new System.Drawing.Size(100, 27);
+ //
+ // tslZeit
+ //
+ this.tslZeit.Name = "tslZeit";
+ this.tslZeit.Text = "Zeitraum:";
+ //
+ // cbRange
+ //
+ this.cbRange.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbRange.Name = "cbRange";
+ this.cbRange.Size = new System.Drawing.Size(110, 27);
+ //
+ // tsRefresh
+ //
+ this.tsRefresh.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+ this.tsRefresh.Name = "tsRefresh";
+ this.tsRefresh.Text = "Aktualisieren";
+ //
+ // tabControlDash
+ //
+ this.tabControlDash.Controls.Add(this.tabDashboard);
+ this.tabControlDash.Controls.Add(this.tabHistory);
+ this.tabControlDash.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tabControlDash.Location = new System.Drawing.Point(0, 27);
+ this.tabControlDash.Name = "tabControlDash";
+ this.tabControlDash.SelectedIndex = 0;
+ this.tabControlDash.Size = new System.Drawing.Size(1200, 623);
+ this.tabControlDash.TabIndex = 1;
+ //
+ // tabDashboard
+ //
+ this.tabDashboard.Controls.Add(this.picEquity);
+ this.tabDashboard.Controls.Add(this.pnlChartsBottom);
+ this.tabDashboard.Controls.Add(this.flpKpis);
+ this.tabDashboard.Location = new System.Drawing.Point(4, 24);
+ this.tabDashboard.Name = "tabDashboard";
+ this.tabDashboard.Padding = new System.Windows.Forms.Padding(3);
+ this.tabDashboard.Size = new System.Drawing.Size(1192, 595);
+ this.tabDashboard.TabIndex = 0;
+ this.tabDashboard.Text = "Dashboard";
+ this.tabDashboard.UseVisualStyleBackColor = true;
+ //
+ // picEquity
+ //
+ this.picEquity.BackColor = System.Drawing.Color.White;
+ this.picEquity.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.picEquity.Location = new System.Drawing.Point(3, 79);
+ this.picEquity.Name = "picEquity";
+ this.picEquity.Size = new System.Drawing.Size(1186, 273);
+ this.picEquity.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.picEquity.TabIndex = 2;
+ this.picEquity.TabStop = false;
+ //
+ // pnlChartsBottom
+ //
+ this.pnlChartsBottom.Controls.Add(this.picDay);
+ this.pnlChartsBottom.Controls.Add(this.picModule);
+ this.pnlChartsBottom.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.pnlChartsBottom.Location = new System.Drawing.Point(3, 352);
+ this.pnlChartsBottom.Name = "pnlChartsBottom";
+ this.pnlChartsBottom.Size = new System.Drawing.Size(1186, 240);
+ this.pnlChartsBottom.TabIndex = 1;
+ //
+ // picDay
+ //
+ this.picDay.BackColor = System.Drawing.Color.White;
+ this.picDay.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.picDay.Location = new System.Drawing.Point(593, 0);
+ this.picDay.Name = "picDay";
+ this.picDay.Size = new System.Drawing.Size(593, 240);
+ this.picDay.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.picDay.TabIndex = 1;
+ this.picDay.TabStop = false;
+ //
+ // picModule
+ //
+ this.picModule.BackColor = System.Drawing.Color.White;
+ this.picModule.Dock = System.Windows.Forms.DockStyle.Left;
+ this.picModule.Location = new System.Drawing.Point(0, 0);
+ this.picModule.Name = "picModule";
+ this.picModule.Size = new System.Drawing.Size(593, 240);
+ this.picModule.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+ this.picModule.TabIndex = 0;
+ this.picModule.TabStop = false;
+ //
+ // flpKpis
+ //
+ this.flpKpis.Controls.Add(this.lblKpiPnl);
+ this.flpKpis.Controls.Add(this.lblKpiWin);
+ this.flpKpis.Controls.Add(this.lblKpiTrades);
+ this.flpKpis.Controls.Add(this.lblKpiAvg);
+ this.flpKpis.Controls.Add(this.lblKpiPf);
+ this.flpKpis.Dock = System.Windows.Forms.DockStyle.Top;
+ this.flpKpis.Location = new System.Drawing.Point(3, 3);
+ this.flpKpis.Name = "flpKpis";
+ this.flpKpis.Padding = new System.Windows.Forms.Padding(4);
+ this.flpKpis.Size = new System.Drawing.Size(1186, 76);
+ this.flpKpis.TabIndex = 0;
+ this.flpKpis.WrapContents = false;
+ //
+ // lblKpiPnl
+ //
+ this.lblKpiPnl.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.lblKpiPnl.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
+ this.lblKpiPnl.Margin = new System.Windows.Forms.Padding(4);
+ this.lblKpiPnl.MinimumSize = new System.Drawing.Size(190, 56);
+ this.lblKpiPnl.Name = "lblKpiPnl";
+ this.lblKpiPnl.Padding = new System.Windows.Forms.Padding(8);
+ this.lblKpiPnl.Size = new System.Drawing.Size(190, 56);
+ this.lblKpiPnl.TabIndex = 0;
+ this.lblKpiPnl.Text = "Netto-PnL\n—";
+ this.lblKpiPnl.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // lblKpiWin
+ //
+ this.lblKpiWin.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.lblKpiWin.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
+ this.lblKpiWin.Margin = new System.Windows.Forms.Padding(4);
+ this.lblKpiWin.MinimumSize = new System.Drawing.Size(150, 56);
+ this.lblKpiWin.Name = "lblKpiWin";
+ this.lblKpiWin.Padding = new System.Windows.Forms.Padding(8);
+ this.lblKpiWin.Size = new System.Drawing.Size(150, 56);
+ this.lblKpiWin.TabIndex = 1;
+ this.lblKpiWin.Text = "Winrate\n—";
+ this.lblKpiWin.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // lblKpiTrades
+ //
+ this.lblKpiTrades.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.lblKpiTrades.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
+ this.lblKpiTrades.Margin = new System.Windows.Forms.Padding(4);
+ this.lblKpiTrades.MinimumSize = new System.Drawing.Size(130, 56);
+ this.lblKpiTrades.Name = "lblKpiTrades";
+ this.lblKpiTrades.Padding = new System.Windows.Forms.Padding(8);
+ this.lblKpiTrades.Size = new System.Drawing.Size(130, 56);
+ this.lblKpiTrades.TabIndex = 2;
+ this.lblKpiTrades.Text = "Trades\n—";
+ this.lblKpiTrades.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // lblKpiAvg
+ //
+ this.lblKpiAvg.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.lblKpiAvg.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
+ this.lblKpiAvg.Margin = new System.Windows.Forms.Padding(4);
+ this.lblKpiAvg.MinimumSize = new System.Drawing.Size(170, 56);
+ this.lblKpiAvg.Name = "lblKpiAvg";
+ this.lblKpiAvg.Padding = new System.Windows.Forms.Padding(8);
+ this.lblKpiAvg.Size = new System.Drawing.Size(170, 56);
+ this.lblKpiAvg.TabIndex = 3;
+ this.lblKpiAvg.Text = "Ø PnL/Trade\n—";
+ this.lblKpiAvg.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // lblKpiPf
+ //
+ this.lblKpiPf.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.lblKpiPf.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Bold);
+ this.lblKpiPf.Margin = new System.Windows.Forms.Padding(4);
+ this.lblKpiPf.MinimumSize = new System.Drawing.Size(150, 56);
+ this.lblKpiPf.Name = "lblKpiPf";
+ this.lblKpiPf.Padding = new System.Windows.Forms.Padding(8);
+ this.lblKpiPf.Size = new System.Drawing.Size(150, 56);
+ this.lblKpiPf.TabIndex = 4;
+ this.lblKpiPf.Text = "Profit-Faktor\n—";
+ this.lblKpiPf.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // tabHistory
+ //
+ this.tabHistory.Controls.Add(this.dgvTrades);
+ this.tabHistory.Controls.Add(this.pnlHistFilters);
+ this.tabHistory.Location = new System.Drawing.Point(4, 24);
+ this.tabHistory.Name = "tabHistory";
+ this.tabHistory.Padding = new System.Windows.Forms.Padding(3);
+ this.tabHistory.Size = new System.Drawing.Size(1192, 595);
+ this.tabHistory.TabIndex = 1;
+ this.tabHistory.Text = "Tradehistorie";
+ this.tabHistory.UseVisualStyleBackColor = true;
//
// dgvTrades
//
@@ -71,23 +296,14 @@ namespace PolyTraderSharp.Ui.Views
this.dgvTrades.AutoGenerateColumns = false;
this.dgvTrades.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dgvTrades.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
- this.colModule,
- this.colAccount,
- this.colMarket,
- this.colOutcome,
- this.colSide,
- this.colEntry,
- this.colExit,
- this.colSize,
- this.colPnl,
- this.colPnlPct,
- this.colClosedAt});
+ this.colModule, this.colAccount, this.colMarket, this.colOutcome, this.colSide,
+ this.colEntry, this.colExit, this.colSize, this.colPnl, this.colPnlPct, this.colClosedAt});
this.dgvTrades.Dock = System.Windows.Forms.DockStyle.Fill;
- this.dgvTrades.Location = new System.Drawing.Point(0, 40);
+ this.dgvTrades.Location = new System.Drawing.Point(3, 39);
this.dgvTrades.Name = "dgvTrades";
this.dgvTrades.ReadOnly = true;
this.dgvTrades.RowHeadersVisible = false;
- this.dgvTrades.Size = new System.Drawing.Size(1100, 560);
+ this.dgvTrades.Size = new System.Drawing.Size(1186, 553);
this.dgvTrades.TabIndex = 1;
//
// colModule
@@ -167,26 +383,108 @@ namespace PolyTraderSharp.Ui.Views
this.colClosedAt.Name = "colClosedAt";
this.colClosedAt.Width = 140;
//
+ // pnlHistFilters
+ //
+ this.pnlHistFilters.Controls.Add(this.cbWinLoss);
+ this.pnlHistFilters.Controls.Add(this.lblErgebnis);
+ this.pnlHistFilters.Controls.Add(this.tbSearch);
+ this.pnlHistFilters.Controls.Add(this.lblSuche);
+ this.pnlHistFilters.Dock = System.Windows.Forms.DockStyle.Top;
+ this.pnlHistFilters.Location = new System.Drawing.Point(3, 3);
+ this.pnlHistFilters.Name = "pnlHistFilters";
+ this.pnlHistFilters.Size = new System.Drawing.Size(1186, 36);
+ this.pnlHistFilters.TabIndex = 0;
+ //
+ // lblSuche
+ //
+ this.lblSuche.AutoSize = true;
+ this.lblSuche.Location = new System.Drawing.Point(6, 9);
+ this.lblSuche.Name = "lblSuche";
+ this.lblSuche.Size = new System.Drawing.Size(45, 15);
+ this.lblSuche.TabIndex = 0;
+ this.lblSuche.Text = "Suche:";
+ //
+ // tbSearch
+ //
+ this.tbSearch.Location = new System.Drawing.Point(57, 6);
+ this.tbSearch.Name = "tbSearch";
+ this.tbSearch.PlaceholderText = "Markt / Outcome …";
+ this.tbSearch.Size = new System.Drawing.Size(280, 23);
+ this.tbSearch.TabIndex = 1;
+ //
+ // lblErgebnis
+ //
+ this.lblErgebnis.AutoSize = true;
+ this.lblErgebnis.Location = new System.Drawing.Point(360, 9);
+ this.lblErgebnis.Name = "lblErgebnis";
+ this.lblErgebnis.Size = new System.Drawing.Size(58, 15);
+ this.lblErgebnis.TabIndex = 2;
+ this.lblErgebnis.Text = "Ergebnis:";
+ //
+ // cbWinLoss
+ //
+ this.cbWinLoss.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbWinLoss.Location = new System.Drawing.Point(424, 6);
+ this.cbWinLoss.Name = "cbWinLoss";
+ this.cbWinLoss.Size = new System.Drawing.Size(150, 23);
+ this.cbWinLoss.TabIndex = 3;
+ //
// DashboardView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.Controls.Add(this.dgvTrades);
- this.Controls.Add(this.pnlTop);
+ this.Controls.Add(this.tabControlDash);
+ this.Controls.Add(this.toolStripDash);
this.ClientSize = new System.Drawing.Size(1200, 650);
this.Name = "DashboardView";
this.Text = "Dashboard";
- this.pnlTop.ResumeLayout(false);
- this.pnlTop.PerformLayout();
+ this.toolStripDash.ResumeLayout(false);
+ this.toolStripDash.PerformLayout();
+ this.tabControlDash.ResumeLayout(false);
+ this.tabDashboard.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.picEquity)).EndInit();
+ this.pnlChartsBottom.ResumeLayout(false);
+ ((System.ComponentModel.ISupportInitialize)(this.picDay)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.picModule)).EndInit();
+ this.flpKpis.ResumeLayout(false);
+ this.tabHistory.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).EndInit();
+ this.pnlHistFilters.ResumeLayout(false);
+ this.pnlHistFilters.PerformLayout();
this.ResumeLayout(false);
+ this.PerformLayout();
}
#endregion
- private System.Windows.Forms.Panel pnlTop;
- private System.Windows.Forms.Button btnRefresh;
- private System.Windows.Forms.Label lblSummary;
+ private System.Windows.Forms.ToolStrip toolStripDash;
+ private System.Windows.Forms.ToolStripLabel tslKonto;
+ private System.Windows.Forms.ToolStripComboBox cbAccount;
+ private System.Windows.Forms.ToolStripLabel tslModul;
+ private System.Windows.Forms.ToolStripComboBox cbModule;
+ private System.Windows.Forms.ToolStripLabel tslArt;
+ private System.Windows.Forms.ToolStripComboBox cbMode;
+ private System.Windows.Forms.ToolStripLabel tslZeit;
+ private System.Windows.Forms.ToolStripComboBox cbRange;
+ private System.Windows.Forms.ToolStripButton tsRefresh;
+ private System.Windows.Forms.TabControl tabControlDash;
+ private System.Windows.Forms.TabPage tabDashboard;
+ private System.Windows.Forms.FlowLayoutPanel flpKpis;
+ private System.Windows.Forms.Label lblKpiPnl;
+ private System.Windows.Forms.Label lblKpiWin;
+ private System.Windows.Forms.Label lblKpiTrades;
+ private System.Windows.Forms.Label lblKpiAvg;
+ private System.Windows.Forms.Label lblKpiPf;
+ private System.Windows.Forms.Panel pnlChartsBottom;
+ private System.Windows.Forms.PictureBox picModule;
+ private System.Windows.Forms.PictureBox picDay;
+ private System.Windows.Forms.PictureBox picEquity;
+ private System.Windows.Forms.TabPage tabHistory;
+ private System.Windows.Forms.Panel pnlHistFilters;
+ private System.Windows.Forms.Label lblSuche;
+ private System.Windows.Forms.TextBox tbSearch;
+ private System.Windows.Forms.Label lblErgebnis;
+ private System.Windows.Forms.ComboBox cbWinLoss;
private System.Windows.Forms.DataGridView dgvTrades;
private System.Windows.Forms.DataGridViewTextBoxColumn colModule;
private System.Windows.Forms.DataGridViewTextBoxColumn colAccount;
diff --git a/Ui/Views/DashboardView.cs b/Ui/Views/DashboardView.cs
index ddeb3d2..3ad6404 100644
--- a/Ui/Views/DashboardView.cs
+++ b/Ui/Views/DashboardView.cs
@@ -1,66 +1,244 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel;
+using System.IO;
using System.Linq;
using System.Windows.Forms;
+using PolyTrader.Core.Analytics;
using PolyTrader.Core.Persistence;
+using PolyTraderSharp.Models;
namespace PolyTraderSharp.Ui.Views
{
///
- /// Modulübergreifendes Dashboard: zeigt die letzten Trades ALLER Module aus dem
- /// generischen Core-Trade-Log (ITradeLogRepository) und eine Kurzauswertung.
- /// Designbar (DashboardView.Designer.cs).
+ /// Modulübergreifendes Dashboard aus dem generischen Core-Trade-Log (ITradeLogRepository).
+ /// Tab „Dashboard": KPI-Kacheln + Charts (Equity-Kurve, PnL je Modul, PnL je Tag) für den im
+ /// ToolStrip gewählten Scope (Konto/Modul/Live-Demo/Zeitraum). Tab „Tradehistorie": gefilterte
+ /// Trade-Liste. Auswertungslogik pur in ; Charts via ScottPlot
+ /// (als Bitmap gerendert – kein WinForms-GL-Control, saubere Dependencies).
///
public partial class DashboardView : Form
{
private ITradeLogRepository? _tradeLog;
private TradingState? _state;
+ private List _allTrades = new(); // zuletzt geladener Roh-Satz
+ private List _historyBase = new(); // Scope-gefiltert, Basis für die Historie-Filter
+ private bool _loading;
+
public DashboardView()
{
InitializeComponent();
+
colEntry.DefaultCellStyle.Format = "F3";
colExit.DefaultCellStyle.Format = "F3";
colSize.DefaultCellStyle.Format = "F2";
colPnl.DefaultCellStyle.Format = "F2";
colPnlPct.DefaultCellStyle.Format = "F1";
colClosedAt.DefaultCellStyle.Format = "dd.MM.yyyy HH:mm";
- btnRefresh.Click += (_, _) => LoadData();
+
+ cbMode.Items.AddRange(new object[] { "Alle", "Live", "Demo" });
+ cbRange.Items.AddRange(new object[] { "7 Tage", "30 Tage", "90 Tage", "Alle" });
+ cbWinLoss.Items.AddRange(new object[] { "Alle", "Gewinner", "Verlierer" });
+
+ tsRefresh.Click += (_, _) => RefreshData();
+ cbAccount.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
+ cbModule.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
+ cbMode.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
+ cbRange.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
+ tbSearch.TextChanged += (_, _) => { if (!_loading) ApplyHistoryFilter(); };
+ cbWinLoss.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyHistoryFilter(); };
}
public void Initialize(ITradeLogRepository tradeLog, TradingState state)
{
_tradeLog = tradeLog;
_state = state;
- LoadData();
+ RefreshData();
}
- private void LoadData()
+ // ===== Daten laden / Scope =====
+
+ private void RefreshData()
{
if (_tradeLog == null) return;
+ try { _allTrades = _tradeLog.GetRecent(5000); }
+ catch { _allTrades = new List(); } // DB nicht bereit -> leer statt Absturz
- var rows = _tradeLog.GetRecent(300).Select(r => new DashboardTradeRow
- {
- Module = r.ModuleName,
- Account = ResolveAccount(r.AccountId, r.IsDemo),
- Market = r.MarketQuestion,
- Outcome = r.Outcome,
- Side = r.Side,
- EntryPrice = r.EntryPrice,
- ExitPrice = r.ExitPrice,
- Size = r.Size,
- RealizedPnl = r.RealizedPnl,
- PnlPercent = r.PnlPercent,
- ClosedAt = r.ClosedAt
- }).ToList();
+ _loading = true;
+ PopulateScopeCombos();
+ _loading = false;
- dgvTrades.DataSource = new BindingList(rows);
-
- decimal totalPnl = rows.Sum(x => x.RealizedPnl);
- var perModule = rows.GroupBy(x => x.Module).Select(g => $"{g.Key}: {g.Count()}");
- lblSummary.Text = $"{rows.Count} Trades | PnL gesamt: {totalPnl:F2} USDC | {string.Join(" · ", perModule)}";
+ ApplyScope();
}
+ private void PopulateScopeCombos()
+ {
+ // Konten
+ var selectedAcc = (cbAccount.SelectedItem as FilterAccount)?.Id ?? -1;
+ cbAccount.Items.Clear();
+ cbAccount.Items.Add(new FilterAccount(-1, "Alle Konten"));
+ if (_state != null)
+ foreach (var a in _state.Accounts.Values.OrderBy(a => a.AccountId))
+ cbAccount.Items.Add(new FilterAccount(a.AccountId,
+ (string.IsNullOrEmpty(a.Name) ? $"#{a.AccountId}" : a.Name) + (a.IsDemo ? " (Demo)" : "")));
+ cbAccount.SelectedIndex = Math.Max(0, IndexOfAccount(selectedAcc));
+
+ // Module (aus den vorhandenen Daten)
+ string selectedMod = cbModule.SelectedItem as string ?? "Alle Module";
+ cbModule.Items.Clear();
+ cbModule.Items.Add("Alle Module");
+ foreach (var m in _allTrades.Select(t => t.ModuleName).Where(m => !string.IsNullOrEmpty(m)).Distinct().OrderBy(m => m))
+ cbModule.Items.Add(m);
+ int modIdx = cbModule.Items.IndexOf(selectedMod);
+ cbModule.SelectedIndex = modIdx >= 0 ? modIdx : 0;
+
+ if (cbMode.SelectedIndex < 0) cbMode.SelectedIndex = 0; // Alle
+ if (cbRange.SelectedIndex < 0) cbRange.SelectedIndex = 1; // 30 Tage
+ if (cbWinLoss.SelectedIndex < 0) cbWinLoss.SelectedIndex = 0;
+ }
+
+ private int IndexOfAccount(int id)
+ {
+ for (int i = 0; i < cbAccount.Items.Count; i++)
+ if (cbAccount.Items[i] is FilterAccount fa && fa.Id == id) return i;
+ return 0;
+ }
+
+ private void ApplyScope()
+ {
+ int accId = (cbAccount.SelectedItem as FilterAccount)?.Id ?? -1;
+ string module = cbModule.SelectedItem as string ?? "Alle Module";
+ string mode = cbMode.SelectedItem as string ?? "Alle";
+ int? days = (cbRange.SelectedItem as string) switch
+ {
+ "7 Tage" => 7,
+ "30 Tage" => 30,
+ "90 Tage" => 90,
+ _ => (int?)null
+ };
+ DateTime? since = days.HasValue ? DateTime.UtcNow.AddDays(-days.Value) : null;
+
+ var scoped = _allTrades.Where(t =>
+ (accId < 0 || t.AccountId == accId) &&
+ (module == "Alle Module" || t.ModuleName == module) &&
+ (mode == "Alle" || (mode == "Live" && !t.IsDemo) || (mode == "Demo" && t.IsDemo)) &&
+ (!since.HasValue || t.ClosedAt >= since.Value)).ToList();
+
+ UpdateKpis(scoped);
+ RenderCharts(scoped);
+
+ _historyBase = scoped
+ .OrderByDescending(t => t.ClosedAt)
+ .Select(ToRow)
+ .ToList();
+ ApplyHistoryFilter();
+ }
+
+ // ===== KPIs =====
+
+ private void UpdateKpis(List scoped)
+ {
+ var k = TradeAnalytics.ComputeKpis(scoped);
+ lblKpiPnl.Text = $"Netto-PnL\n{k.NetPnl:N2} USDC";
+ lblKpiPnl.ForeColor = k.NetPnl >= 0 ? System.Drawing.Color.ForestGreen : System.Drawing.Color.Firebrick;
+ lblKpiWin.Text = $"Winrate\n{k.WinRatePct:N1} %";
+ lblKpiTrades.Text = $"Trades\n{k.TradeCount}";
+ lblKpiAvg.Text = $"Ø PnL/Trade\n{k.AvgPnlPerTrade:N2}";
+ lblKpiPf.Text = $"Profit-Faktor\n{(k.ProfitFactor >= TradeAnalytics.NoLossProfitFactor ? "∞" : k.ProfitFactor.ToString("N2"))}";
+ }
+
+ // ===== Charts (ScottPlot -> Bitmap) =====
+
+ private void RenderCharts(List scoped)
+ {
+ RenderPlot(picEquity, plot =>
+ {
+ var curve = TradeAnalytics.EquityCurve(scoped);
+ plot.Title("Equity-Kurve (kumulierter PnL)");
+ if (curve.Count == 0) return;
+ double[] xs = curve.Select(p => p.At.ToOADate()).ToArray();
+ double[] ys = curve.Select(p => (double)p.Cumulative).ToArray();
+ plot.Add.Scatter(xs, ys);
+ plot.Axes.DateTimeTicksBottom();
+ });
+
+ RenderPlot(picModule, plot =>
+ {
+ var byMod = TradeAnalytics.PnlByKey(scoped, t => string.IsNullOrEmpty(t.ModuleName) ? "—" : t.ModuleName);
+ plot.Title("PnL je Modul");
+ if (byMod.Count == 0) return;
+ plot.Add.Bars(byMod.Select(x => (double)x.Pnl).ToArray());
+ SetCategoryTicks(plot, byMod.Select(x => x.Key).ToArray());
+ });
+
+ RenderPlot(picDay, plot =>
+ {
+ var byDay = TradeAnalytics.PnlByDay(scoped);
+ plot.Title("PnL je Tag");
+ if (byDay.Count == 0) return;
+ plot.Add.Bars(byDay.Select(x => (double)x.Pnl).ToArray());
+ SetCategoryTicks(plot, byDay.Select(x => x.Day.ToString("dd.MM")).ToArray());
+ });
+ }
+
+ private static void SetCategoryTicks(ScottPlot.Plot plot, string[] labels)
+ {
+ var ticks = new ScottPlot.TickGenerators.NumericManual();
+ for (int i = 0; i < labels.Length; i++)
+ ticks.AddMajor(i, labels[i]);
+ plot.Axes.Bottom.TickGenerator = ticks;
+ }
+
+ private static void RenderPlot(PictureBox pic, Action build)
+ {
+ int w = Math.Max(pic.ClientSize.Width, 300);
+ int h = Math.Max(pic.ClientSize.Height, 200);
+ var plot = new ScottPlot.Plot();
+ try { build(plot); }
+ catch { /* Chart-Rendering darf die UI nie killen */ }
+
+ byte[] png = plot.GetImage(w, h).GetImageBytes();
+ using var ms = new MemoryStream(png);
+ using var img = System.Drawing.Image.FromStream(ms);
+ var old = pic.Image;
+ pic.Image = new System.Drawing.Bitmap(img);
+ old?.Dispose();
+ }
+
+ // ===== Tradehistorie =====
+
+ private void ApplyHistoryFilter()
+ {
+ string search = tbSearch.Text.Trim();
+ string winLoss = cbWinLoss.SelectedItem as string ?? "Alle";
+
+ IEnumerable rows = _historyBase;
+ if (search.Length > 0)
+ rows = rows.Where(r =>
+ (r.Market?.Contains(search, StringComparison.OrdinalIgnoreCase) ?? false) ||
+ (r.Outcome?.Contains(search, StringComparison.OrdinalIgnoreCase) ?? false));
+ if (winLoss == "Gewinner") rows = rows.Where(r => r.RealizedPnl > 0m);
+ else if (winLoss == "Verlierer") rows = rows.Where(r => r.RealizedPnl < 0m);
+
+ dgvTrades.DataSource = new BindingList(rows.ToList());
+ }
+
+ private DashboardTradeRow ToRow(TradeRecord r) => new()
+ {
+ Module = r.ModuleName,
+ Account = ResolveAccount(r.AccountId, r.IsDemo),
+ Market = r.MarketQuestion,
+ Outcome = r.Outcome,
+ Side = r.Side,
+ EntryPrice = r.EntryPrice,
+ ExitPrice = r.ExitPrice,
+ Size = r.Size,
+ RealizedPnl = r.RealizedPnl,
+ PnlPercent = r.PnlPercent,
+ ClosedAt = r.ClosedAt
+ };
+
private string ResolveAccount(int accountId, bool isDemo)
{
string suffix = isDemo ? " (Demo)" : "";
@@ -68,9 +246,14 @@ namespace PolyTraderSharp.Ui.Views
return acc.Name + suffix;
return $"#{accountId}{suffix}";
}
+
+ private sealed record FilterAccount(int Id, string Label)
+ {
+ public override string ToString() => Label;
+ }
}
- /// Anzeige-Zeile für das Dashboard-Grid (Account bereits zu Name aufgelöst).
+ /// Anzeige-Zeile für das Historie-Grid (Account bereits zu Name aufgelöst).
public class DashboardTradeRow
{
public string Module { get; set; } = string.Empty;