diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
index 91ec227..3e77472 100644
--- a/Properties/Resources.Designer.cs
+++ b/Properties/Resources.Designer.cs
@@ -259,5 +259,15 @@ namespace PolyTraderSharp.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
+
+ ///
+ /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap warning {
+ get {
+ object obj = ResourceManager.GetObject("warning", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
}
}
diff --git a/Properties/Resources.resx b/Properties/Resources.resx
index f13c12e..27bfc0f 100644
--- a/Properties/Resources.resx
+++ b/Properties/Resources.resx
@@ -121,23 +121,23 @@
..\Resources\system_time.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\error_log.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
..\Resources\diskette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
..\Resources\money.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
- ..\Resources\traffic_lights_green.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
..\Resources\money_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
..\Resources\token_quantifier.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
- ..\Resources\error_log.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\traffic_lights_green.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
..\Resources\cancel.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
@@ -148,6 +148,9 @@
..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\dashboard.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
..\Resources\coins_in_hand.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
@@ -175,7 +178,7 @@
..\Resources\money_dollar.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
-
- ..\Resources\dashboard.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ ..\Resources\warning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
\ No newline at end of file
diff --git a/Resources/warning.png b/Resources/warning.png
new file mode 100644
index 0000000..311e726
Binary files /dev/null and b/Resources/warning.png differ
diff --git a/src/PolyTrader.Modules.CopyTrading/Logic/TradeRowColoring.cs b/src/PolyTrader.Modules.CopyTrading/Logic/TradeRowColoring.cs
new file mode 100644
index 0000000..e6f607e
--- /dev/null
+++ b/src/PolyTrader.Modules.CopyTrading/Logic/TradeRowColoring.cs
@@ -0,0 +1,18 @@
+using System.Drawing;
+
+namespace PolyTrader.Modules.CopyTrading.Logic
+{
+ ///
+ /// Reine Zeilenfärbung nach realisiertem/unrealisiertem PnL-Prozent (getestet, damit die
+ /// Schwellen belastbar sind): Verlust rot, 0–10 % hellgrün, > 10 % kräftiges Grün.
+ ///
+ public static class TradeRowColoring
+ {
+ public static readonly Color Loss = Color.FromArgb(245, 200, 200); // < 0 % – rot
+ public static readonly Color SmallWin = Color.FromArgb(212, 240, 212); // 0–10 % – hellgrün
+ public static readonly Color BigWin = Color.FromArgb(140, 214, 140); // > 10 % – grün
+
+ public static Color ForPnlPercent(decimal pnlPercent) =>
+ pnlPercent < 0m ? Loss : (pnlPercent > 10m ? BigWin : SmallWin);
+ }
+}
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.Designer.cs b/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.Designer.cs
index 85ce0aa..d3c3494 100644
--- a/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.Designer.cs
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.Designer.cs
@@ -35,9 +35,22 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.colClosedAt = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.statusPanel = new System.Windows.Forms.Panel();
this.lblSummary = new System.Windows.Forms.Label();
+ this.pnlFilters = new System.Windows.Forms.Panel();
+ this.lblMarkt = new System.Windows.Forms.Label();
+ this.tbMarket = new System.Windows.Forms.TextBox();
+ this.lblMaster = new System.Windows.Forms.Label();
+ this.cbMaster = new System.Windows.Forms.ComboBox();
+ this.lblErgebnis = new System.Windows.Forms.Label();
+ this.cbResult = new System.Windows.Forms.ComboBox();
+ this.lblVon = new System.Windows.Forms.Label();
+ this.dtFrom = new System.Windows.Forms.DateTimePicker();
+ this.lblBis = new System.Windows.Forms.Label();
+ this.dtTo = new System.Windows.Forms.DateTimePicker();
+ this.btnReset = new System.Windows.Forms.Button();
this.toolbar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).BeginInit();
this.statusPanel.SuspendLayout();
+ this.pnlFilters.SuspendLayout();
this.SuspendLayout();
//
// toolbar
@@ -210,12 +223,126 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.lblSummary.Text = "—";
this.lblSummary.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
+ // pnlFilters
+ //
+ this.pnlFilters.Controls.Add(this.lblMarkt);
+ this.pnlFilters.Controls.Add(this.tbMarket);
+ this.pnlFilters.Controls.Add(this.lblMaster);
+ this.pnlFilters.Controls.Add(this.cbMaster);
+ this.pnlFilters.Controls.Add(this.lblErgebnis);
+ this.pnlFilters.Controls.Add(this.cbResult);
+ this.pnlFilters.Controls.Add(this.lblVon);
+ this.pnlFilters.Controls.Add(this.dtFrom);
+ this.pnlFilters.Controls.Add(this.lblBis);
+ this.pnlFilters.Controls.Add(this.dtTo);
+ this.pnlFilters.Controls.Add(this.btnReset);
+ this.pnlFilters.Dock = System.Windows.Forms.DockStyle.Top;
+ this.pnlFilters.Location = new System.Drawing.Point(0, 25);
+ this.pnlFilters.Name = "pnlFilters";
+ this.pnlFilters.Size = new System.Drawing.Size(1100, 34);
+ this.pnlFilters.TabIndex = 3;
+ //
+ // lblMarkt
+ //
+ this.lblMarkt.AutoSize = true;
+ this.lblMarkt.Location = new System.Drawing.Point(6, 9);
+ this.lblMarkt.Name = "lblMarkt";
+ this.lblMarkt.Size = new System.Drawing.Size(41, 15);
+ this.lblMarkt.Text = "Markt:";
+ //
+ // tbMarket
+ //
+ this.tbMarket.Location = new System.Drawing.Point(51, 6);
+ this.tbMarket.Name = "tbMarket";
+ this.tbMarket.PlaceholderText = "Suchtext …";
+ this.tbMarket.Size = new System.Drawing.Size(200, 23);
+ this.tbMarket.TabIndex = 0;
+ //
+ // lblMaster
+ //
+ this.lblMaster.AutoSize = true;
+ this.lblMaster.Location = new System.Drawing.Point(263, 9);
+ this.lblMaster.Name = "lblMaster";
+ this.lblMaster.Size = new System.Drawing.Size(48, 15);
+ this.lblMaster.Text = "Master:";
+ //
+ // cbMaster
+ //
+ this.cbMaster.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbMaster.Location = new System.Drawing.Point(315, 6);
+ this.cbMaster.Name = "cbMaster";
+ this.cbMaster.Size = new System.Drawing.Size(170, 23);
+ this.cbMaster.TabIndex = 1;
+ //
+ // lblErgebnis
+ //
+ this.lblErgebnis.AutoSize = true;
+ this.lblErgebnis.Location = new System.Drawing.Point(497, 9);
+ this.lblErgebnis.Name = "lblErgebnis";
+ this.lblErgebnis.Size = new System.Drawing.Size(58, 15);
+ this.lblErgebnis.Text = "Ergebnis:";
+ //
+ // cbResult
+ //
+ this.cbResult.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbResult.Items.AddRange(new object[] { "Alle", "Gewinner", "Verlierer" });
+ this.cbResult.Location = new System.Drawing.Point(559, 6);
+ this.cbResult.Name = "cbResult";
+ this.cbResult.Size = new System.Drawing.Size(110, 23);
+ this.cbResult.TabIndex = 2;
+ //
+ // lblVon
+ //
+ this.lblVon.AutoSize = true;
+ this.lblVon.Location = new System.Drawing.Point(681, 9);
+ this.lblVon.Name = "lblVon";
+ this.lblVon.Size = new System.Drawing.Size(31, 15);
+ this.lblVon.Text = "Von:";
+ //
+ // dtFrom
+ //
+ this.dtFrom.Checked = false;
+ this.dtFrom.Format = System.Windows.Forms.DateTimePickerFormat.Short;
+ this.dtFrom.Location = new System.Drawing.Point(714, 6);
+ this.dtFrom.Name = "dtFrom";
+ this.dtFrom.ShowCheckBox = true;
+ this.dtFrom.Size = new System.Drawing.Size(120, 23);
+ this.dtFrom.TabIndex = 3;
+ //
+ // lblBis
+ //
+ this.lblBis.AutoSize = true;
+ this.lblBis.Location = new System.Drawing.Point(840, 9);
+ this.lblBis.Name = "lblBis";
+ this.lblBis.Size = new System.Drawing.Size(26, 15);
+ this.lblBis.Text = "Bis:";
+ //
+ // dtTo
+ //
+ this.dtTo.Checked = false;
+ this.dtTo.Format = System.Windows.Forms.DateTimePickerFormat.Short;
+ this.dtTo.Location = new System.Drawing.Point(868, 6);
+ this.dtTo.Name = "dtTo";
+ this.dtTo.ShowCheckBox = true;
+ this.dtTo.Size = new System.Drawing.Size(120, 23);
+ this.dtTo.TabIndex = 4;
+ //
+ // btnReset
+ //
+ this.btnReset.Location = new System.Drawing.Point(994, 5);
+ this.btnReset.Name = "btnReset";
+ this.btnReset.Size = new System.Drawing.Size(90, 25);
+ this.btnReset.TabIndex = 5;
+ this.btnReset.Text = "Zurücksetzen";
+ this.btnReset.UseVisualStyleBackColor = true;
+ //
// ClosedTradesView
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.dgvTrades);
this.Controls.Add(this.statusPanel);
+ this.Controls.Add(this.pnlFilters);
this.Controls.Add(this.toolbar);
this.Name = "ClosedTradesView";
this.Size = new System.Drawing.Size(1100, 600);
@@ -223,6 +350,8 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.toolbar.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).EndInit();
this.statusPanel.ResumeLayout(false);
+ this.pnlFilters.ResumeLayout(false);
+ this.pnlFilters.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
@@ -247,5 +376,17 @@ namespace PolyTrader.Modules.CopyTrading.Ui
private System.Windows.Forms.DataGridViewTextBoxColumn colClosedAt;
private System.Windows.Forms.Panel statusPanel;
private System.Windows.Forms.Label lblSummary;
+ private System.Windows.Forms.Panel pnlFilters;
+ private System.Windows.Forms.Label lblMarkt;
+ private System.Windows.Forms.TextBox tbMarket;
+ private System.Windows.Forms.Label lblMaster;
+ private System.Windows.Forms.ComboBox cbMaster;
+ private System.Windows.Forms.Label lblErgebnis;
+ private System.Windows.Forms.ComboBox cbResult;
+ private System.Windows.Forms.Label lblVon;
+ private System.Windows.Forms.DateTimePicker dtFrom;
+ private System.Windows.Forms.Label lblBis;
+ private System.Windows.Forms.DateTimePicker dtTo;
+ private System.Windows.Forms.Button btnReset;
}
}
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.cs b/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.cs
index 8b98021..a8e6348 100644
--- a/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.cs
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/ClosedTradesView.cs
@@ -1,7 +1,9 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
+using PolyTrader.Modules.CopyTrading.Logic;
using PolyTrader.Modules.CopyTrading.Persistence;
using PolyTraderSharp;
using PolyTraderSharp.Models;
@@ -19,6 +21,9 @@ namespace PolyTrader.Modules.CopyTrading.Ui
private TradingState? _state;
private CopyTradingState? _copyState;
+ private List _allRows = new();
+ private bool _loading;
+
// Parameterloser Konstruktor für den WinForms-Designer.
public ClosedTradesView()
{
@@ -33,6 +38,18 @@ namespace PolyTrader.Modules.CopyTrading.Ui
colClosedAt.DefaultCellStyle.Format = "dd.MM.yyyy HH:mm";
tsRefresh.Click += (_, _) => LoadData();
+
+ // Zeilenfärbung nach PnL-% (grün/hellgrün/rot) nach jedem (Neu-)Binden.
+ dgvTrades.DataBindingComplete += (_, _) => ColorRows();
+
+ // Filter: Markt (Text), Master (Combo), Ergebnis (Win/Loss), Datum (Von/Bis, optional).
+ tbMarket.TextChanged += (_, _) => ApplyFilter();
+ cbMaster.SelectedIndexChanged += (_, _) => ApplyFilter();
+ cbResult.SelectedIndexChanged += (_, _) => ApplyFilter();
+ dtFrom.ValueChanged += (_, _) => ApplyFilter();
+ dtTo.ValueChanged += (_, _) => ApplyFilter();
+ btnReset.Click += (_, _) => ResetFilters();
+ if (cbResult.Items.Count > 0) cbResult.SelectedIndex = 0;
}
/// Injiziert die Abhängigkeiten (nach der DI-Auflösung) und lädt die Daten.
@@ -48,7 +65,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
{
if (_tradeLog == null) return;
- var rows = _tradeLog.Find(_ => true)
+ _allRows = _tradeLog.Find(_ => true)
.OrderByDescending(t => t.ClosedAt)
.Select(t => new ClosedTradeRow
{
@@ -75,12 +92,71 @@ namespace PolyTrader.Modules.CopyTrading.Ui
})
.ToList();
+ PopulateMasters();
+ ApplyFilter();
+ }
+
+ private void PopulateMasters()
+ {
+ _loading = true;
+ string prev = cbMaster.SelectedItem as string ?? "Alle";
+ cbMaster.Items.Clear();
+ cbMaster.Items.Add("Alle");
+ foreach (var m in _allRows.Select(r => r.SourceTraderName)
+ .Where(s => !string.IsNullOrEmpty(s)).Distinct().OrderBy(s => s))
+ cbMaster.Items.Add(m);
+ int idx = cbMaster.Items.IndexOf(prev);
+ cbMaster.SelectedIndex = idx >= 0 ? idx : 0;
+ _loading = false;
+ }
+
+ private void ApplyFilter()
+ {
+ if (_loading) return;
+
+ IEnumerable q = _allRows;
+
+ string market = tbMarket.Text.Trim();
+ if (market.Length > 0)
+ q = q.Where(r => (r.MarketQuestion?.Contains(market, StringComparison.OrdinalIgnoreCase) ?? false)
+ || (r.MarketSlug?.Contains(market, StringComparison.OrdinalIgnoreCase) ?? false));
+
+ if (cbMaster.SelectedItem is string master && master != "Alle")
+ q = q.Where(r => r.SourceTraderName == master);
+
+ string result = cbResult.SelectedItem as string ?? "Alle";
+ if (result == "Gewinner") q = q.Where(r => r.RealizedPnl > 0m);
+ else if (result == "Verlierer") q = q.Where(r => r.RealizedPnl < 0m);
+
+ if (dtFrom.Checked) { DateTime f = dtFrom.Value.Date; q = q.Where(r => r.ClosedAt >= f); }
+ if (dtTo.Checked) { DateTime t = dtTo.Value.Date.AddDays(1).AddTicks(-1); q = q.Where(r => r.ClosedAt <= t); }
+
+ var rows = q.ToList();
dgvTrades.DataSource = new BindingList(rows);
decimal totalPnl = rows.Sum(x => x.RealizedPnl);
int wins = rows.Count(x => x.RealizedPnl > 0);
double winrate = rows.Count > 0 ? (double)wins / rows.Count * 100 : 0;
- lblSummary.Text = $"{rows.Count} Trades | PnL gesamt: {totalPnl:F2} USDC | Winrate: {winrate:F1}%";
+ lblSummary.Text = $"{rows.Count} / {_allRows.Count} Trades | PnL: {totalPnl:F2} USDC | Winrate: {winrate:F1}%";
+ }
+
+ private void ResetFilters()
+ {
+ _loading = true;
+ tbMarket.Text = string.Empty;
+ if (cbMaster.Items.Count > 0) cbMaster.SelectedIndex = 0;
+ if (cbResult.Items.Count > 0) cbResult.SelectedIndex = 0;
+ dtFrom.Checked = false;
+ dtTo.Checked = false;
+ _loading = false;
+ ApplyFilter();
+ }
+
+ private void ColorRows()
+ {
+ foreach (DataGridViewRow row in dgvTrades.Rows)
+ if (row.DataBoundItem is ClosedTradeRow r)
+ row.DefaultCellStyle.BackColor = TradeRowColoring.ForPnlPercent(r.PnlPercent);
}
private string ResolveAccount(int accountId, bool isDemo)
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.Designer.cs b/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.Designer.cs
index a5071ca..91eee2c 100644
--- a/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.Designer.cs
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.Designer.cs
@@ -19,13 +19,16 @@ namespace PolyTrader.Modules.CopyTrading.Ui
{
this.tabControl = new System.Windows.Forms.TabControl();
this.tabMasters = new System.Windows.Forms.TabPage();
+ this.tabOpen = new System.Windows.Forms.TabPage();
this.tabClosed = new System.Windows.Forms.TabPage();
this.tabSettings = new System.Windows.Forms.TabPage();
this.masterTradersView = new PolyTrader.Modules.CopyTrading.Ui.MasterTradersView();
+ this.openTradesView = new PolyTrader.Modules.CopyTrading.Ui.OpenTradesView();
this.closedTradesView = new PolyTrader.Modules.CopyTrading.Ui.ClosedTradesView();
this.accountSettingsView = new PolyTrader.Modules.CopyTrading.Ui.AccountSettingsView();
this.tabControl.SuspendLayout();
this.tabMasters.SuspendLayout();
+ this.tabOpen.SuspendLayout();
this.tabClosed.SuspendLayout();
this.tabSettings.SuspendLayout();
this.SuspendLayout();
@@ -33,6 +36,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
// tabControl
//
this.tabControl.Controls.Add(this.tabMasters);
+ this.tabControl.Controls.Add(this.tabOpen);
this.tabControl.Controls.Add(this.tabClosed);
this.tabControl.Controls.Add(this.tabSettings);
this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
@@ -53,6 +57,25 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.tabMasters.Text = "Master-Trader";
this.tabMasters.UseVisualStyleBackColor = true;
//
+ // tabOpen
+ //
+ this.tabOpen.Controls.Add(this.openTradesView);
+ this.tabOpen.Location = new System.Drawing.Point(4, 24);
+ this.tabOpen.Name = "tabOpen";
+ this.tabOpen.Padding = new System.Windows.Forms.Padding(3);
+ this.tabOpen.Size = new System.Drawing.Size(1192, 672);
+ this.tabOpen.TabIndex = 3;
+ this.tabOpen.Text = "Offene Trades";
+ this.tabOpen.UseVisualStyleBackColor = true;
+ //
+ // openTradesView
+ //
+ this.openTradesView.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.openTradesView.Location = new System.Drawing.Point(3, 3);
+ this.openTradesView.Name = "openTradesView";
+ this.openTradesView.Size = new System.Drawing.Size(1186, 666);
+ this.openTradesView.TabIndex = 0;
+ //
// tabClosed
//
this.tabClosed.Controls.Add(this.closedTradesView);
@@ -111,6 +134,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.Text = "Copytrading";
this.tabControl.ResumeLayout(false);
this.tabMasters.ResumeLayout(false);
+ this.tabOpen.ResumeLayout(false);
this.tabClosed.ResumeLayout(false);
this.tabSettings.ResumeLayout(false);
this.ResumeLayout(false);
@@ -120,9 +144,11 @@ namespace PolyTrader.Modules.CopyTrading.Ui
private System.Windows.Forms.TabControl tabControl;
private System.Windows.Forms.TabPage tabMasters;
+ private System.Windows.Forms.TabPage tabOpen;
private System.Windows.Forms.TabPage tabClosed;
private System.Windows.Forms.TabPage tabSettings;
private MasterTradersView masterTradersView;
+ private OpenTradesView openTradesView;
private ClosedTradesView closedTradesView;
private AccountSettingsView accountSettingsView;
}
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.cs b/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.cs
index d6e69d0..345c82d 100644
--- a/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.cs
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/CopyTradingMainForm.cs
@@ -29,6 +29,8 @@ namespace PolyTrader.Modules.CopyTrading.Ui
masterTradersView.Initialize(
services.GetRequiredService(), state, copyState);
+ openTradesView.Initialize(state, copyState);
+
closedTradesView.Initialize(
services.GetRequiredService(), state, copyState);
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.Designer.cs b/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.Designer.cs
index ca82405..01544e0 100644
--- a/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.Designer.cs
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.Designer.cs
@@ -102,6 +102,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.grid.AllowUserToAddRows = false;
this.grid.AllowUserToDeleteRows = false;
this.grid.AutoGenerateColumns = false;
+ this.grid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.colId,
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.cs b/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.cs
index 0f0c88e..0493ef0 100644
--- a/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.cs
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/MasterTradersView.cs
@@ -35,6 +35,21 @@ namespace PolyTrader.Modules.CopyTrading.Ui
colCopyPf.DefaultCellStyle.Format = "F2";
colCopyAvg.DefaultCellStyle.Format = "F2";
+ // Spaltenbreiten proportional (grid-weites Fill): keine überlaufenden Fixbreiten mehr,
+ // die Spalten teilen sich immer exakt die verfügbare Breite (Fix des „verbuggten" Grids).
+ colId.FillWeight = 40;
+ colWallet.FillWeight = 150;
+ colName.FillWeight = 200;
+ colCategory.FillWeight = 90;
+ colActive.FillWeight = 50;
+ colTrades.FillWeight = 85;
+ colWinrate.FillWeight = 90;
+ colPnl.FillWeight = 90;
+ colCopyPnl.FillWeight = 110;
+ colCopyPf.FillWeight = 95;
+ colCopyAvg.FillWeight = 95;
+ colCopyCount.FillWeight = 95;
+
tsNew.Click += (_, _) => AddNew();
tsSave.Click += (_, _) => SaveCurrent();
tsDelete.Click += (_, _) => DeleteCurrent();
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/OpenTradesView.Designer.cs b/src/PolyTrader.Modules.CopyTrading/Ui/OpenTradesView.Designer.cs
new file mode 100644
index 0000000..4c79126
--- /dev/null
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/OpenTradesView.Designer.cs
@@ -0,0 +1,241 @@
+namespace PolyTrader.Modules.CopyTrading.Ui
+{
+ partial class OpenTradesView
+ {
+ private System.ComponentModel.IContainer components = null;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Vom Komponenten-Designer generierter Code
+
+ private void InitializeComponent()
+ {
+ this.toolbar = new System.Windows.Forms.ToolStrip();
+ this.tsRefresh = new System.Windows.Forms.ToolStripButton();
+ this.dgvOpen = new System.Windows.Forms.DataGridView();
+ this.colAccount = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colTrader = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colMarket = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colOutcome = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colSide = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colEntry = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colCurrent = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colSize = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colAmount = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colValue = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colUnrealized = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colUnrealizedPct = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.colStatus = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ this.statusPanel = new System.Windows.Forms.Panel();
+ this.lblSummary = new System.Windows.Forms.Label();
+ this.toolbar.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.dgvOpen)).BeginInit();
+ this.statusPanel.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // toolbar
+ //
+ this.toolbar.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
+ this.toolbar.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.tsRefresh });
+ this.toolbar.Location = new System.Drawing.Point(0, 0);
+ this.toolbar.Name = "toolbar";
+ this.toolbar.Size = new System.Drawing.Size(1100, 25);
+ this.toolbar.TabIndex = 0;
+ //
+ // tsRefresh
+ //
+ this.tsRefresh.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+ this.tsRefresh.Name = "tsRefresh";
+ this.tsRefresh.Size = new System.Drawing.Size(90, 22);
+ this.tsRefresh.Text = "Aktualisieren";
+ //
+ // dgvOpen
+ //
+ this.dgvOpen.AllowUserToAddRows = false;
+ this.dgvOpen.AllowUserToDeleteRows = false;
+ this.dgvOpen.AutoGenerateColumns = false;
+ this.dgvOpen.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
+ this.dgvOpen.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.dgvOpen.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+ this.colAccount, this.colTrader, this.colMarket, this.colOutcome, this.colSide,
+ this.colEntry, this.colCurrent, this.colSize, this.colAmount, this.colValue,
+ this.colUnrealized, this.colUnrealizedPct, this.colStatus});
+ this.dgvOpen.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.dgvOpen.Location = new System.Drawing.Point(0, 25);
+ this.dgvOpen.Name = "dgvOpen";
+ this.dgvOpen.ReadOnly = true;
+ this.dgvOpen.RowHeadersVisible = false;
+ this.dgvOpen.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
+ this.dgvOpen.Size = new System.Drawing.Size(1100, 549);
+ this.dgvOpen.TabIndex = 1;
+ //
+ // colAccount
+ //
+ this.colAccount.DataPropertyName = "AccountName";
+ this.colAccount.HeaderText = "Account";
+ this.colAccount.Name = "colAccount";
+ this.colAccount.ReadOnly = true;
+ this.colAccount.FillWeight = 120;
+ //
+ // colTrader
+ //
+ this.colTrader.DataPropertyName = "SourceTraderName";
+ this.colTrader.HeaderText = "Master-Trader";
+ this.colTrader.Name = "colTrader";
+ this.colTrader.ReadOnly = true;
+ this.colTrader.FillWeight = 120;
+ //
+ // colMarket
+ //
+ this.colMarket.DataPropertyName = "MarketQuestion";
+ this.colMarket.HeaderText = "Markt";
+ this.colMarket.Name = "colMarket";
+ this.colMarket.ReadOnly = true;
+ this.colMarket.FillWeight = 240;
+ //
+ // colOutcome
+ //
+ this.colOutcome.DataPropertyName = "Outcome";
+ this.colOutcome.HeaderText = "Outcome";
+ this.colOutcome.Name = "colOutcome";
+ this.colOutcome.ReadOnly = true;
+ this.colOutcome.FillWeight = 80;
+ //
+ // colSide
+ //
+ this.colSide.DataPropertyName = "Side";
+ this.colSide.HeaderText = "Side";
+ this.colSide.Name = "colSide";
+ this.colSide.ReadOnly = true;
+ this.colSide.FillWeight = 55;
+ //
+ // colEntry
+ //
+ this.colEntry.DataPropertyName = "EntryPrice";
+ this.colEntry.HeaderText = "Entry";
+ this.colEntry.Name = "colEntry";
+ this.colEntry.ReadOnly = true;
+ this.colEntry.FillWeight = 65;
+ //
+ // colCurrent
+ //
+ this.colCurrent.DataPropertyName = "CurrentPrice";
+ this.colCurrent.HeaderText = "Aktuell";
+ this.colCurrent.Name = "colCurrent";
+ this.colCurrent.ReadOnly = true;
+ this.colCurrent.FillWeight = 65;
+ //
+ // colSize
+ //
+ this.colSize.DataPropertyName = "Size";
+ this.colSize.HeaderText = "Size";
+ this.colSize.Name = "colSize";
+ this.colSize.ReadOnly = true;
+ this.colSize.FillWeight = 70;
+ //
+ // colAmount
+ //
+ this.colAmount.DataPropertyName = "AmountUsd";
+ this.colAmount.HeaderText = "Einsatz";
+ this.colAmount.Name = "colAmount";
+ this.colAmount.ReadOnly = true;
+ this.colAmount.FillWeight = 80;
+ //
+ // colValue
+ //
+ this.colValue.DataPropertyName = "CurrentValueUsd";
+ this.colValue.HeaderText = "Wert";
+ this.colValue.Name = "colValue";
+ this.colValue.ReadOnly = true;
+ this.colValue.FillWeight = 80;
+ //
+ // colUnrealized
+ //
+ this.colUnrealized.DataPropertyName = "UnrealizedPnl";
+ this.colUnrealized.HeaderText = "Buchgewinn";
+ this.colUnrealized.Name = "colUnrealized";
+ this.colUnrealized.ReadOnly = true;
+ this.colUnrealized.FillWeight = 90;
+ //
+ // colUnrealizedPct
+ //
+ this.colUnrealizedPct.DataPropertyName = "UnrealizedPct";
+ this.colUnrealizedPct.HeaderText = "Buchgew. %";
+ this.colUnrealizedPct.Name = "colUnrealizedPct";
+ this.colUnrealizedPct.ReadOnly = true;
+ this.colUnrealizedPct.FillWeight = 80;
+ //
+ // colStatus
+ //
+ this.colStatus.DataPropertyName = "Status";
+ this.colStatus.HeaderText = "Status";
+ this.colStatus.Name = "colStatus";
+ this.colStatus.ReadOnly = true;
+ this.colStatus.FillWeight = 90;
+ //
+ // statusPanel
+ //
+ this.statusPanel.Controls.Add(this.lblSummary);
+ this.statusPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
+ this.statusPanel.Location = new System.Drawing.Point(0, 574);
+ this.statusPanel.Name = "statusPanel";
+ this.statusPanel.Size = new System.Drawing.Size(1100, 26);
+ this.statusPanel.TabIndex = 2;
+ //
+ // lblSummary
+ //
+ this.lblSummary.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.lblSummary.Location = new System.Drawing.Point(0, 0);
+ this.lblSummary.Name = "lblSummary";
+ this.lblSummary.Padding = new System.Windows.Forms.Padding(8, 0, 0, 0);
+ this.lblSummary.Size = new System.Drawing.Size(1100, 26);
+ this.lblSummary.TabIndex = 0;
+ this.lblSummary.Text = "—";
+ this.lblSummary.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // OpenTradesView
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.dgvOpen);
+ this.Controls.Add(this.statusPanel);
+ this.Controls.Add(this.toolbar);
+ this.Name = "OpenTradesView";
+ this.Size = new System.Drawing.Size(1100, 600);
+ this.toolbar.ResumeLayout(false);
+ this.toolbar.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.dgvOpen)).EndInit();
+ this.statusPanel.ResumeLayout(false);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ToolStrip toolbar;
+ private System.Windows.Forms.ToolStripButton tsRefresh;
+ private System.Windows.Forms.DataGridView dgvOpen;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colAccount;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colTrader;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colMarket;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colOutcome;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colSide;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colEntry;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colCurrent;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colSize;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colAmount;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colValue;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colUnrealized;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colUnrealizedPct;
+ private System.Windows.Forms.DataGridViewTextBoxColumn colStatus;
+ private System.Windows.Forms.Panel statusPanel;
+ private System.Windows.Forms.Label lblSummary;
+ }
+}
diff --git a/src/PolyTrader.Modules.CopyTrading/Ui/OpenTradesView.cs b/src/PolyTrader.Modules.CopyTrading/Ui/OpenTradesView.cs
new file mode 100644
index 0000000..ad94ea0
--- /dev/null
+++ b/src/PolyTrader.Modules.CopyTrading/Ui/OpenTradesView.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Forms;
+using PolyTrader.Modules.CopyTrading.Logic;
+using PolyTraderSharp;
+using PolyTraderSharp.Models;
+
+namespace PolyTrader.Modules.CopyTrading.Ui
+{
+ ///
+ /// Listet ALLE aktuell offenen Positionen (Portfolios der Accounts) mit Buchgewinn/-verlust.
+ /// Quelle ist der Laufzeit-State (), nicht die DB – so ist die
+ /// Anzeige live. Zeilenfärbung nach Buchgewinn-% ().
+ /// Layout im Designer (OpenTradesView.Designer.cs).
+ ///
+ public partial class OpenTradesView : UserControl
+ {
+ private TradingState? _state;
+ private CopyTradingState? _copyState;
+
+ public OpenTradesView()
+ {
+ InitializeComponent();
+
+ colEntry.DefaultCellStyle.Format = "F3";
+ colCurrent.DefaultCellStyle.Format = "F3";
+ colSize.DefaultCellStyle.Format = "F2";
+ colAmount.DefaultCellStyle.Format = "F2";
+ colValue.DefaultCellStyle.Format = "F2";
+ colUnrealized.DefaultCellStyle.Format = "F2";
+ colUnrealizedPct.DefaultCellStyle.Format = "F1";
+
+ tsRefresh.Click += (_, _) => LoadData();
+ dgvOpen.DataBindingComplete += (_, _) => ColorRows();
+ }
+
+ public void Initialize(TradingState state, CopyTradingState copyState)
+ {
+ _state = state;
+ _copyState = copyState;
+ LoadData();
+ }
+
+ private void LoadData()
+ {
+ if (_state == null) return;
+
+ var rows = _state.Accounts.Values
+ .SelectMany(a => a.OpenPositions.Values.Select(p => (acc: a, pos: p)))
+ .OrderByDescending(x => x.pos.CurrentValueUsd)
+ .Select(x =>
+ {
+ decimal unrealized = x.pos.CurrentValueUsd - x.pos.AmountUsd;
+ decimal pct = x.pos.AmountUsd > 0m ? unrealized / x.pos.AmountUsd * 100m : 0m;
+ return new OpenTradeRow
+ {
+ AccountName = ResolveAccount(x.acc.AccountId, x.pos.IsDemo),
+ SourceTraderName = ResolveTrader(x.pos.SourceTraderId, x.pos.SourceTraderName),
+ MarketQuestion = x.pos.MarketQuestion,
+ Outcome = x.pos.Outcome,
+ Side = x.pos.Side,
+ EntryPrice = x.pos.EntryPrice,
+ CurrentPrice = x.pos.CurrentPrice,
+ Size = x.pos.Size,
+ AmountUsd = x.pos.AmountUsd,
+ CurrentValueUsd = x.pos.CurrentValueUsd,
+ UnrealizedPnl = unrealized,
+ UnrealizedPct = pct,
+ Status = x.pos.ExitPending ? "Exit läuft" : "offen"
+ };
+ })
+ .ToList();
+
+ dgvOpen.DataSource = new BindingList(rows);
+
+ decimal totalValue = rows.Sum(r => r.CurrentValueUsd);
+ decimal totalUnrealized = rows.Sum(r => r.UnrealizedPnl);
+ lblSummary.Text = $"{rows.Count} offene Positionen | Wert: {totalValue:F2} USDC | Buchgewinn: {totalUnrealized:F2} USDC";
+ }
+
+ private void ColorRows()
+ {
+ foreach (DataGridViewRow row in dgvOpen.Rows)
+ if (row.DataBoundItem is OpenTradeRow r)
+ row.DefaultCellStyle.BackColor = TradeRowColoring.ForPnlPercent(r.UnrealizedPct);
+ }
+
+ private string ResolveAccount(int accountId, bool isDemo)
+ {
+ string suffix = isDemo ? " (Demo)" : "";
+ if (_state != null && _state.Accounts.TryGetValue(accountId, out var acc) && !string.IsNullOrEmpty(acc.Name))
+ return acc.Name + suffix;
+ return $"#{accountId}{suffix}";
+ }
+
+ private string ResolveTrader(int traderId, string fallback)
+ {
+ if (_copyState != null && _copyState.Traders.TryGetValue(traderId, out var t) && !string.IsNullOrEmpty(t.DisplayName))
+ return t.DisplayName;
+ return !string.IsNullOrEmpty(fallback) ? fallback : (traderId > 0 ? $"#{traderId}" : "System");
+ }
+
+ /// Anzeige-Zeile für offene Positionen.
+ private sealed class OpenTradeRow
+ {
+ public string AccountName { get; set; } = string.Empty;
+ public string SourceTraderName { get; set; } = string.Empty;
+ public string MarketQuestion { get; set; } = string.Empty;
+ public string Outcome { get; set; } = string.Empty;
+ public string Side { get; set; } = string.Empty;
+ public decimal EntryPrice { get; set; }
+ public decimal CurrentPrice { get; set; }
+ public decimal Size { get; set; }
+ public decimal AmountUsd { get; set; }
+ public decimal CurrentValueUsd { get; set; }
+ public decimal UnrealizedPnl { get; set; }
+ public decimal UnrealizedPct { get; set; }
+ public string Status { get; set; } = string.Empty;
+ }
+ }
+}
diff --git a/tests/PolyTrader.Tests/TradeRowColoringTests.cs b/tests/PolyTrader.Tests/TradeRowColoringTests.cs
new file mode 100644
index 0000000..5864944
--- /dev/null
+++ b/tests/PolyTrader.Tests/TradeRowColoringTests.cs
@@ -0,0 +1,29 @@
+using PolyTrader.Modules.CopyTrading.Logic;
+using Xunit;
+
+namespace PolyTrader.Tests
+{
+ /// Sicherheitsnetz für die PnL-Zeilenfärbung (Slice 3): Schwellen -10/0/10.
+ public class TradeRowColoringTests
+ {
+ [Theory]
+ [InlineData(-0.01, "loss")]
+ [InlineData(-25.0, "loss")]
+ [InlineData(0.0, "small")] // Break-even = hellgrün (kein Verlust)
+ [InlineData(5.0, "small")]
+ [InlineData(10.0, "small")] // genau 10 % noch hellgrün
+ [InlineData(10.01, "big")]
+ [InlineData(80.0, "big")]
+ public void Color_matches_threshold(double pct, string expected)
+ {
+ var c = TradeRowColoring.ForPnlPercent((decimal)pct);
+ var want = expected switch
+ {
+ "loss" => TradeRowColoring.Loss,
+ "big" => TradeRowColoring.BigWin,
+ _ => TradeRowColoring.SmallWin
+ };
+ Assert.Equal(want, c);
+ }
+ }
+}