P1b: System.Drawing aus der Fachlogik entfernt

- TradeRowColoring liefert jetzt eine TradeRowTint-Kategorie (Loss/SmallWin/BigWin)
  statt einer System.Drawing.Color. Die Schwellenlogik bleibt getestet, die konkrete
  Farbe legt die UI fest (neu: Ui/TradeRowPalette.cs im CopyTrading-Modul).
  Bessere Schichtung und Voraussetzung dafuer, dass das Modul spaeter net8.0 wird.
- TradeRowColoringTests prueft die Kategorie statt der Farbe (gleiche Abdeckung).
- Models/DashboardRow.cs: verwaistes using System.Drawing entfernt.

System.Drawing liegt damit ausschliesslich noch in UI-Ordnern - die Fachlogik in
Core und Modulen ist frei davon.

442 Tests gruen, --smoke-ui gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-06 17:13:14 +02:00
co-authored by Claude Opus 5
parent ad1593bfc1
commit 5320d25d6d
6 changed files with 71 additions and 32 deletions
+1 -2
View File
@@ -1,5 +1,4 @@
using System.ComponentModel; using System.ComponentModel;
using System.Drawing;
namespace PolyTraderSharp.Models namespace PolyTraderSharp.Models
{ {
@@ -1,18 +1,34 @@
using System.Drawing;
namespace PolyTrader.Modules.CopyTrading.Logic namespace PolyTrader.Modules.CopyTrading.Logic
{ {
/// <summary>Farbkategorie einer Trade-Zeile. Die konkrete Farbe legt die jeweilige UI fest.</summary>
public enum TradeRowTint
{
/// <summary>&lt; 0 % Verlust.</summary>
Loss,
/// <summary>010 % kleiner Gewinn.</summary>
SmallWin,
/// <summary>&gt; 10 % deutlicher Gewinn.</summary>
BigWin
}
/// <summary> /// <summary>
/// Reine Zeilenfärbung nach realisiertem/unrealisiertem PnL-Prozent (getestet, damit die /// Reine Einstufung von Trade-Zeilen nach realisiertem/unrealisiertem PnL-Prozent (getestet,
/// Schwellen belastbar sind): Verlust rot, 010 % hellgrün, > 10 % kräftiges Grün. /// damit die Schwellen belastbar sind): Verlust, 010 %, &gt; 10 %.
///
/// <para>Liefert bewusst eine <see cref="TradeRowTint"/>-Kategorie statt einer Farbe: Die
/// Modul-Logik ist damit frei von <c>System.Drawing</c> (seit .NET 7 Windows-only) und die
/// Zuordnung Kategorie → Farbe liegt dort, wo sie hingehört in der jeweiligen Oberfläche.</para>
/// </summary> /// </summary>
public static class TradeRowColoring public static class TradeRowColoring
{ {
public static readonly Color Loss = Color.FromArgb(245, 200, 200); // < 0 % rot /// <summary>Schwelle, ab der ein Gewinn als deutlich gilt (in Prozent, exklusiv).</summary>
public static readonly Color SmallWin = Color.FromArgb(212, 240, 212); // 010 % hellgrün public const decimal BigWinThresholdPercent = 10m;
public static readonly Color BigWin = Color.FromArgb(140, 214, 140); // > 10 % grün
public static Color ForPnlPercent(decimal pnlPercent) => public static TradeRowTint ForPnlPercent(decimal pnlPercent) =>
pnlPercent < 0m ? Loss : (pnlPercent > 10m ? BigWin : SmallWin); pnlPercent < 0m
? TradeRowTint.Loss
: (pnlPercent > BigWinThresholdPercent ? TradeRowTint.BigWin : TradeRowTint.SmallWin);
} }
} }
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
@@ -156,7 +156,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
{ {
foreach (DataGridViewRow row in dgvTrades.Rows) foreach (DataGridViewRow row in dgvTrades.Rows)
if (row.DataBoundItem is ClosedTradeRow r) if (row.DataBoundItem is ClosedTradeRow r)
row.DefaultCellStyle.BackColor = TradeRowColoring.ForPnlPercent(r.PnlPercent); row.DefaultCellStyle.BackColor = TradeRowPalette.ForPnlPercent(r.PnlPercent);
} }
private string ResolveAccount(int accountId, bool isDemo) private string ResolveAccount(int accountId, bool isDemo)
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
@@ -84,7 +84,7 @@ namespace PolyTrader.Modules.CopyTrading.Ui
{ {
foreach (DataGridViewRow row in dgvOpen.Rows) foreach (DataGridViewRow row in dgvOpen.Rows)
if (row.DataBoundItem is OpenTradeRow r) if (row.DataBoundItem is OpenTradeRow r)
row.DefaultCellStyle.BackColor = TradeRowColoring.ForPnlPercent(r.UnrealizedPct); row.DefaultCellStyle.BackColor = TradeRowPalette.ForPnlPercent(r.UnrealizedPct);
} }
private string ResolveAccount(int accountId, bool isDemo) private string ResolveAccount(int accountId, bool isDemo)
@@ -0,0 +1,27 @@
using System.Drawing;
using PolyTrader.Modules.CopyTrading.Logic;
namespace PolyTrader.Modules.CopyTrading.Ui
{
/// <summary>
/// WinForms-Farbpalette zu den <see cref="TradeRowTint"/>-Kategorien. Die Schwellenlogik liegt
/// (getestet) in <see cref="TradeRowColoring"/>; hier steht nur noch, wie die Kategorien aussehen.
/// Die Avalonia-UI bekommt später ihre eigene Palette zu denselben Kategorien.
/// </summary>
internal static class TradeRowPalette
{
public static readonly Color Loss = Color.FromArgb(245, 200, 200); // rot
public static readonly Color SmallWin = Color.FromArgb(212, 240, 212); // hellgrün
public static readonly Color BigWin = Color.FromArgb(140, 214, 140); // grün
public static Color For(TradeRowTint tint) => tint switch
{
TradeRowTint.Loss => Loss,
TradeRowTint.BigWin => BigWin,
_ => SmallWin
};
/// <summary>Bequemlichkeit für die Grids: PnL-Prozent direkt in die Zeilenfarbe.</summary>
public static Color ForPnlPercent(decimal pnlPercent) => For(TradeRowColoring.ForPnlPercent(pnlPercent));
}
}
+14 -17
View File
@@ -3,27 +3,24 @@ using Xunit;
namespace PolyTrader.Tests namespace PolyTrader.Tests
{ {
/// <summary>Sicherheitsnetz für die PnL-Zeilenfärbung (Slice 3): Schwellen -10/0/10.</summary> /// <summary>
/// Sicherheitsnetz für die PnL-Zeileneinstufung (Slice 3): Schwellen 0 und 10 %.
/// Prüft seit der Linux-Portierung die Kategorie statt der Farbe die Schwellenlogik ist
/// das Fachliche, die konkrete Farbe Sache der jeweiligen UI (siehe TradeRowPalette).
/// </summary>
public class TradeRowColoringTests public class TradeRowColoringTests
{ {
[Theory] [Theory]
[InlineData(-0.01, "loss")] [InlineData(-0.01, TradeRowTint.Loss)]
[InlineData(-25.0, "loss")] [InlineData(-25.0, TradeRowTint.Loss)]
[InlineData(0.0, "small")] // Break-even = hellgrün (kein Verlust) [InlineData(0.0, TradeRowTint.SmallWin)] // Break-even = kein Verlust
[InlineData(5.0, "small")] [InlineData(5.0, TradeRowTint.SmallWin)]
[InlineData(10.0, "small")] // genau 10 % noch hellgrün [InlineData(10.0, TradeRowTint.SmallWin)] // genau 10 % noch kleiner Gewinn
[InlineData(10.01, "big")] [InlineData(10.01, TradeRowTint.BigWin)]
[InlineData(80.0, "big")] [InlineData(80.0, TradeRowTint.BigWin)]
public void Color_matches_threshold(double pct, string expected) public void Tint_matches_threshold(double pct, TradeRowTint expected)
{ {
var c = TradeRowColoring.ForPnlPercent((decimal)pct); Assert.Equal(expected, TradeRowColoring.ForPnlPercent((decimal)pct));
var want = expected switch
{
"loss" => TradeRowColoring.Loss,
"big" => TradeRowColoring.BigWin,
_ => TradeRowColoring.SmallWin
};
Assert.Equal(want, c);
} }
} }
} }