UI Slice 3: CopyTrading - Zeilenfaerbung, Filter, Offene-Trades-Tab, Master-Grid-Fix
- Geschlossene Trades: Zeilenfaerbung nach PnL-% (TradeRowColoring, pur+getestet: <0 rot, 0-10% hellgruen, >10% gruen) via DataBindingComplete. Filter-Panel (Designer): Markt (Text), Master-Trader (Combo, dynamisch), Ergebnis (Alle/Gewinner/Verlierer), Von/Bis (optionale DateTimePicker mit Checkbox), Zuruecksetzen. Summary zeigt gefiltert/gesamt. - Neuer Tab 'Offene Trades' (OpenTradesView, designerfaehig): alle offenen Positionen aus dem Laufzeit-State mit Entry/Aktuell/Wert/Buchgewinn/-% und Status (offen/Exit laeuft), Zeilenfaerbung nach Buchgewinn-%, Summenzeile. In CopyTradingMainForm zwischen Master-Trader und Geschlossene eingehaengt. - Master-Trader-Grid: AutoSizeColumnsMode=Fill + FillWeights -> Spaltenbreiten teilen sich immer die Breite (Fix der 'verbuggten' Breiten); RowHeader war bereits aus. Tests: +8 (TradeRowColoring-Schwellen). Build 0 Fehler, 396 Tests gruen, --smoke-ui gruen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
25f0d34042
commit
0725a81dbd
Generated
+10
@@ -259,5 +259,15 @@ namespace PolyTraderSharp.Properties {
|
|||||||
return ((System.Drawing.Bitmap)(obj));
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap warning {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("warning", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,23 +121,23 @@
|
|||||||
<data name="system_time" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="system_time" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\system_time.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\system_time.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="error_log" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\error_log.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
<data name="diskette" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="diskette" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\diskette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\diskette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="money" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="money" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\money.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\money.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="traffic_lights_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
|
||||||
<value>..\Resources\traffic_lights_green.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
|
||||||
</data>
|
|
||||||
<data name="money_add" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="money_add" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\money_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\money_add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="token_quantifier" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="token_quantifier" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\token_quantifier.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\token_quantifier.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="error_log" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="traffic_lights_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\error_log.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\traffic_lights_green.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="cancel" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="cancel" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\cancel.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\cancel.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
@@ -148,6 +148,9 @@
|
|||||||
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="dashboard" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\dashboard.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
<data name="coins_in_hand" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="coins_in_hand" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\coins_in_hand.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\coins_in_hand.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -175,7 +178,7 @@
|
|||||||
<data name="money_dollar" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="money_dollar" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\money_dollar.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\money_dollar.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="dashboard" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="warning" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\dashboard.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\warning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace PolyTrader.Modules.CopyTrading.Logic
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,9 +35,22 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
this.colClosedAt = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.colClosedAt = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.statusPanel = new System.Windows.Forms.Panel();
|
this.statusPanel = new System.Windows.Forms.Panel();
|
||||||
this.lblSummary = new System.Windows.Forms.Label();
|
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();
|
this.toolbar.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).BeginInit();
|
||||||
this.statusPanel.SuspendLayout();
|
this.statusPanel.SuspendLayout();
|
||||||
|
this.pnlFilters.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// toolbar
|
// toolbar
|
||||||
@@ -210,12 +223,126 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
this.lblSummary.Text = "—";
|
this.lblSummary.Text = "—";
|
||||||
this.lblSummary.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
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
|
// ClosedTradesView
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.Controls.Add(this.dgvTrades);
|
this.Controls.Add(this.dgvTrades);
|
||||||
this.Controls.Add(this.statusPanel);
|
this.Controls.Add(this.statusPanel);
|
||||||
|
this.Controls.Add(this.pnlFilters);
|
||||||
this.Controls.Add(this.toolbar);
|
this.Controls.Add(this.toolbar);
|
||||||
this.Name = "ClosedTradesView";
|
this.Name = "ClosedTradesView";
|
||||||
this.Size = new System.Drawing.Size(1100, 600);
|
this.Size = new System.Drawing.Size(1100, 600);
|
||||||
@@ -223,6 +350,8 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
this.toolbar.PerformLayout();
|
this.toolbar.PerformLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.dgvTrades)).EndInit();
|
||||||
this.statusPanel.ResumeLayout(false);
|
this.statusPanel.ResumeLayout(false);
|
||||||
|
this.pnlFilters.ResumeLayout(false);
|
||||||
|
this.pnlFilters.PerformLayout();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
}
|
}
|
||||||
@@ -247,5 +376,17 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
private System.Windows.Forms.DataGridViewTextBoxColumn colClosedAt;
|
private System.Windows.Forms.DataGridViewTextBoxColumn colClosedAt;
|
||||||
private System.Windows.Forms.Panel statusPanel;
|
private System.Windows.Forms.Panel statusPanel;
|
||||||
private System.Windows.Forms.Label lblSummary;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using PolyTrader.Modules.CopyTrading.Logic;
|
||||||
using PolyTrader.Modules.CopyTrading.Persistence;
|
using PolyTrader.Modules.CopyTrading.Persistence;
|
||||||
using PolyTraderSharp;
|
using PolyTraderSharp;
|
||||||
using PolyTraderSharp.Models;
|
using PolyTraderSharp.Models;
|
||||||
@@ -19,6 +21,9 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
private TradingState? _state;
|
private TradingState? _state;
|
||||||
private CopyTradingState? _copyState;
|
private CopyTradingState? _copyState;
|
||||||
|
|
||||||
|
private List<ClosedTradeRow> _allRows = new();
|
||||||
|
private bool _loading;
|
||||||
|
|
||||||
// Parameterloser Konstruktor für den WinForms-Designer.
|
// Parameterloser Konstruktor für den WinForms-Designer.
|
||||||
public ClosedTradesView()
|
public ClosedTradesView()
|
||||||
{
|
{
|
||||||
@@ -33,6 +38,18 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
colClosedAt.DefaultCellStyle.Format = "dd.MM.yyyy HH:mm";
|
colClosedAt.DefaultCellStyle.Format = "dd.MM.yyyy HH:mm";
|
||||||
|
|
||||||
tsRefresh.Click += (_, _) => LoadData();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Injiziert die Abhängigkeiten (nach der DI-Auflösung) und lädt die Daten.</summary>
|
/// <summary>Injiziert die Abhängigkeiten (nach der DI-Auflösung) und lädt die Daten.</summary>
|
||||||
@@ -48,7 +65,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
{
|
{
|
||||||
if (_tradeLog == null) return;
|
if (_tradeLog == null) return;
|
||||||
|
|
||||||
var rows = _tradeLog.Find(_ => true)
|
_allRows = _tradeLog.Find(_ => true)
|
||||||
.OrderByDescending(t => t.ClosedAt)
|
.OrderByDescending(t => t.ClosedAt)
|
||||||
.Select(t => new ClosedTradeRow
|
.Select(t => new ClosedTradeRow
|
||||||
{
|
{
|
||||||
@@ -75,12 +92,71 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
})
|
})
|
||||||
.ToList();
|
.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<ClosedTradeRow> 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<ClosedTradeRow>(rows);
|
dgvTrades.DataSource = new BindingList<ClosedTradeRow>(rows);
|
||||||
|
|
||||||
decimal totalPnl = rows.Sum(x => x.RealizedPnl);
|
decimal totalPnl = rows.Sum(x => x.RealizedPnl);
|
||||||
int wins = rows.Count(x => x.RealizedPnl > 0);
|
int wins = rows.Count(x => x.RealizedPnl > 0);
|
||||||
double winrate = rows.Count > 0 ? (double)wins / rows.Count * 100 : 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)
|
private string ResolveAccount(int accountId, bool isDemo)
|
||||||
|
|||||||
@@ -19,13 +19,16 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
{
|
{
|
||||||
this.tabControl = new System.Windows.Forms.TabControl();
|
this.tabControl = new System.Windows.Forms.TabControl();
|
||||||
this.tabMasters = new System.Windows.Forms.TabPage();
|
this.tabMasters = new System.Windows.Forms.TabPage();
|
||||||
|
this.tabOpen = new System.Windows.Forms.TabPage();
|
||||||
this.tabClosed = new System.Windows.Forms.TabPage();
|
this.tabClosed = new System.Windows.Forms.TabPage();
|
||||||
this.tabSettings = new System.Windows.Forms.TabPage();
|
this.tabSettings = new System.Windows.Forms.TabPage();
|
||||||
this.masterTradersView = new PolyTrader.Modules.CopyTrading.Ui.MasterTradersView();
|
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.closedTradesView = new PolyTrader.Modules.CopyTrading.Ui.ClosedTradesView();
|
||||||
this.accountSettingsView = new PolyTrader.Modules.CopyTrading.Ui.AccountSettingsView();
|
this.accountSettingsView = new PolyTrader.Modules.CopyTrading.Ui.AccountSettingsView();
|
||||||
this.tabControl.SuspendLayout();
|
this.tabControl.SuspendLayout();
|
||||||
this.tabMasters.SuspendLayout();
|
this.tabMasters.SuspendLayout();
|
||||||
|
this.tabOpen.SuspendLayout();
|
||||||
this.tabClosed.SuspendLayout();
|
this.tabClosed.SuspendLayout();
|
||||||
this.tabSettings.SuspendLayout();
|
this.tabSettings.SuspendLayout();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
@@ -33,6 +36,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
// tabControl
|
// tabControl
|
||||||
//
|
//
|
||||||
this.tabControl.Controls.Add(this.tabMasters);
|
this.tabControl.Controls.Add(this.tabMasters);
|
||||||
|
this.tabControl.Controls.Add(this.tabOpen);
|
||||||
this.tabControl.Controls.Add(this.tabClosed);
|
this.tabControl.Controls.Add(this.tabClosed);
|
||||||
this.tabControl.Controls.Add(this.tabSettings);
|
this.tabControl.Controls.Add(this.tabSettings);
|
||||||
this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
@@ -53,6 +57,25 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
this.tabMasters.Text = "Master-Trader";
|
this.tabMasters.Text = "Master-Trader";
|
||||||
this.tabMasters.UseVisualStyleBackColor = true;
|
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
|
// tabClosed
|
||||||
//
|
//
|
||||||
this.tabClosed.Controls.Add(this.closedTradesView);
|
this.tabClosed.Controls.Add(this.closedTradesView);
|
||||||
@@ -111,6 +134,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
this.Text = "Copytrading";
|
this.Text = "Copytrading";
|
||||||
this.tabControl.ResumeLayout(false);
|
this.tabControl.ResumeLayout(false);
|
||||||
this.tabMasters.ResumeLayout(false);
|
this.tabMasters.ResumeLayout(false);
|
||||||
|
this.tabOpen.ResumeLayout(false);
|
||||||
this.tabClosed.ResumeLayout(false);
|
this.tabClosed.ResumeLayout(false);
|
||||||
this.tabSettings.ResumeLayout(false);
|
this.tabSettings.ResumeLayout(false);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
@@ -120,9 +144,11 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
|
|
||||||
private System.Windows.Forms.TabControl tabControl;
|
private System.Windows.Forms.TabControl tabControl;
|
||||||
private System.Windows.Forms.TabPage tabMasters;
|
private System.Windows.Forms.TabPage tabMasters;
|
||||||
|
private System.Windows.Forms.TabPage tabOpen;
|
||||||
private System.Windows.Forms.TabPage tabClosed;
|
private System.Windows.Forms.TabPage tabClosed;
|
||||||
private System.Windows.Forms.TabPage tabSettings;
|
private System.Windows.Forms.TabPage tabSettings;
|
||||||
private MasterTradersView masterTradersView;
|
private MasterTradersView masterTradersView;
|
||||||
|
private OpenTradesView openTradesView;
|
||||||
private ClosedTradesView closedTradesView;
|
private ClosedTradesView closedTradesView;
|
||||||
private AccountSettingsView accountSettingsView;
|
private AccountSettingsView accountSettingsView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
masterTradersView.Initialize(
|
masterTradersView.Initialize(
|
||||||
services.GetRequiredService<ITrackedTraderRepository>(), state, copyState);
|
services.GetRequiredService<ITrackedTraderRepository>(), state, copyState);
|
||||||
|
|
||||||
|
openTradesView.Initialize(state, copyState);
|
||||||
|
|
||||||
closedTradesView.Initialize(
|
closedTradesView.Initialize(
|
||||||
services.GetRequiredService<ICopyTradeLogRepository>(), state, copyState);
|
services.GetRequiredService<ICopyTradeLogRepository>(), state, copyState);
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
this.grid.AllowUserToAddRows = false;
|
this.grid.AllowUserToAddRows = false;
|
||||||
this.grid.AllowUserToDeleteRows = false;
|
this.grid.AllowUserToDeleteRows = false;
|
||||||
this.grid.AutoGenerateColumns = false;
|
this.grid.AutoGenerateColumns = false;
|
||||||
|
this.grid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
|
||||||
this.grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
this.grid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||||
this.grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
this.grid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||||
this.colId,
|
this.colId,
|
||||||
|
|||||||
@@ -35,6 +35,21 @@ namespace PolyTrader.Modules.CopyTrading.Ui
|
|||||||
colCopyPf.DefaultCellStyle.Format = "F2";
|
colCopyPf.DefaultCellStyle.Format = "F2";
|
||||||
colCopyAvg.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();
|
tsNew.Click += (_, _) => AddNew();
|
||||||
tsSave.Click += (_, _) => SaveCurrent();
|
tsSave.Click += (_, _) => SaveCurrent();
|
||||||
tsDelete.Click += (_, _) => DeleteCurrent();
|
tsDelete.Click += (_, _) => DeleteCurrent();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Listet ALLE aktuell offenen Positionen (Portfolios der Accounts) mit Buchgewinn/-verlust.
|
||||||
|
/// Quelle ist der Laufzeit-State (<see cref="TradingState.Accounts"/>), nicht die DB – so ist die
|
||||||
|
/// Anzeige live. Zeilenfärbung nach Buchgewinn-% (<see cref="TradeRowColoring"/>).
|
||||||
|
/// Layout im Designer (OpenTradesView.Designer.cs).
|
||||||
|
/// </summary>
|
||||||
|
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<OpenTradeRow>(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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Anzeige-Zeile für offene Positionen.</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using PolyTrader.Modules.CopyTrading.Logic;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PolyTrader.Tests
|
||||||
|
{
|
||||||
|
/// <summary>Sicherheitsnetz für die PnL-Zeilenfärbung (Slice 3): Schwellen -10/0/10.</summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user