Files
PolyTraderSharp/docs/ANALYSE-Linux-Portierung.md
T
RichardandClaude Opus 5 ac5e16f0a6 Plandokumente auf den tatsaechlichen Codestand gezogen
Beide Portierungsdokumente hingen dem Code hinterher. Commit 7f0b05e setzte A1
bis A4 um, ohne eine einzige Markdown-Datei mitzuziehen - der Leitfaden wies die
vier Punkte weiter als offen aus. Ein Agent, der ihn als Arbeitsanweisung nimmt,
haette sie ein zweites Mal gebaut.

LEITFADEN-Avalonia-Portierung.md:
- A1 bis A4 als erledigt markiert, mit Ist-Tabelle: wo der Code liegt und dass
  auch die Feinheiten sitzen (nullables GetService fuer Supervisor, LaunchUriAsync
  statt Process.Start, DialogWindow.Confirm vor Delete, ContextMenu mit beiden
  Eintraegen). Die Soll-Beschreibungen bleiben als Pruefvorlage fuer A5 stehen.
- A5 (Durchsehen mit echten Daten) als einziger offener Punkt ausgewiesen.
- Abschnitt B um das Lizenz-Gate und den Watchdog-Heartbeat ergaenzt. Wichtig fuer
  den Naechsten: die Avalonia-App setzt derzeit GAR KEINE Lizenz durch, waehrend
  die WinForms-Fassung das Gate weiter hat. Beides kommt mit dem Deploymentcenter
  neu, also dort nichts mehr investieren.
- Neuer Abschnitt C fuer Folgepunkte ohne Blockerwirkung.

ANALYSE-Linux-Portierung.md (Revision 5):
- P5 bis P8 als erledigt, Stufen L3 und L4 entsprechend; Restaufwand von 33-51 PT
  auf 7-13 PT korrigiert.
- Offen bleiben P3c (Deploymentcenter, steht als Naechstes), CI, systemd/Feldtest,
  WinForms-Ausbau und die Abnahme A5.
- Historische Aufwandszahlen als Rueckblick gekennzeichnet statt geloescht.

Nachgeprueft statt uebernommen: A1-A4 gegen den Code verifiziert, ebenso dass es
keinerlei CI-Konfiguration gibt (weder .github noch .gitea) und dass --headless in
App.Avalonia/Program.cs bereits existiert.

Beim Pruefen aufgefallen, kein Fehler: TerminalLogger schreibt die Tagesdatei mit
DateTime.Now und der Launcher liest sie mit DateTime.Now - konsistent, aber beide
ignorieren die in P2 eingefuehrte AppTimeZone. Als Folgepunkt C notiert, relevant
erst beim Headless-Betrieb auf einem UTC-Server.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 21:59:10 +02:00

44 KiB
Raw Blame History

Analyse & Umsetzungsrahmen: Linux-Fähigkeit von PolyTrader

Stand: 13.08.2026 · Revision 5 (Avalonia-Portierung P5P8 durch; offen sind Deploymentcenter, CI, systemd/Feldtest, WinForms-Ausbau) Basis: 8 Projekte auf net10.0 (PolyTrader.App als net10.0-windows noch als Fallback), 450 Tests

Als Nächstes: die Integration des überarbeiteten Deploymentcenters (P3c). Sie ersetzt Lizenzverwaltung, Watchdog und Error-Reporting grundlegend. Alles, was in diesem Dokument zu LicenseLabrador oder zum Watchdog-Heartbeat steht, ist damit Übergangszustand — dort nichts mehr ausbauen.


Entscheidungen (06.08.2026)

# Thema Entscheidung Auswirkung
1 Kultur-Bug Sofort gefixt Erledigt, siehe Abschnitt 4.1
2 Designer-Regel Neue Formulierung übernommen: Layout deklarativ in .axaml, nicht zur Laufzeit im Code ersetzt die WinForms-Designer-Regel
3 Windows-UI Variante B: Modul-UI entfernt , Core-UI folgt; Fallback ist der Tag winforms-final umgesetzt 06.08.2026
4 PropertyGrid wird durch normale Steuerelemente ersetzt +5 PT ggü. Fremdbibliothek, dafür bessere UX
5 Lizenz/Watchdog laufen jetzt über das Deploymentcenter, nicht mehr über Einzeldienste Linux-Blocker entfällt, siehe 5.2
6 Zeitzone einmalig bei der Installation festgelegt, nicht im laufenden Betrieb gewechselt vereinfacht die Umsetzung, siehe 3.3
7 Threema entfernt ; Nachfolger: RocketChat + Telegram umgesetzt am 06.08.2026, siehe 5.3
8 Charts LiveCharts2 bestätigt (nach Klarstellung, dass die Integration nicht komplex ist) +1 PT ggü. ScottPlot.Avalonia

Netto-Effekt auf den Gesamtaufwand: damals auf 4161 PT geschätzt. Überholt — Stand 13.08.2026 sind noch 713 PT offen, weil die Avalonia-Portierung (P5P8) inzwischen durch ist; siehe Abschnitt 7.


0. Kurzfassung

Frage Antwort
Ist es machbar? Ja, ohne architektonische Sackgassen.
Was ist der Löwenanteil? Die UI. ~8.300 LOC (34 % des Produktivcodes) hängen an WinForms.
Was ist überraschend gut? Die gesamte Trading-Kernlogik ist bereits portabel: kein einziger DllImport, keine Registry, keine WMI, keine DPAPI, keine SpecialFolder, saubere Path.Combine-Nutzung. Nethereum, Pomelo/EF, HttpClient, ClientWebSocket, AES-GCM laufen unverändert.
Was war der teuerste Einzelpunkt? Der PropertyGrid-Ersatz — erledigt, ersetzt durch den kategorisierten SettingsEditor.
Gesamtaufwand ~713 Personentage offen (Stand 13.08.2026). Die UI-Portierung ist inhaltlich durch (P5P8); offen sind nur noch P3c Deploymentcenter, CI, systemd/Feldtest, der WinForms-Ausbau und die Abnahme A5.
Wichtigste Erkenntnis Galt: „Für ~15 PT bekommst du 80 % des Nutzens." Eingetreten und überholt — die UI wurde nicht als Fallback eingefroren, sondern vollständig nach Avalonia portiert. Der Rückfallstand liegt im Tag winforms-final. Siehe Abschnitt 7.

1. Bestandsaufnahme

1.1 Projektstruktur und Zielframeworks

Projekt TargetFramework UseWindowsForms Warum WinForms?
PolyTrader.App net8.0-windows7.0 (WinExe) ja Shell, Launcher, 4 Core-Views
PolyTrader.Core net8.0-windows ja nur UI-Contract (ModuleUi.cs, WindowMenu.cs)
Modules.CopyTrading net8.0-windows ja 5 Views + TradeRowColoring (System.Drawing.Color)
Modules.ResolutionFarming net8.0-windows ja 1 Fenster
Modules.Supervisor net8.0-windows ja 1 Fenster
Modules.Accounting net8.0-windows ja 1 Fenster
PolyTrader.Tests net8.0-windows ja nur weil die Referenzen -windows sind
IcgSoftware.Threema.CoreMsgApi (lib) entfernt am 06.08.2026 (Entscheidung 7)

Alle verbleibenden Projekte sind -windows aber die Bindung ist dünn. Core und Module brauchen WinForms ausschließlich für den UI-Contract; die Services darunter sind plattformneutral.

1.2 Wo sitzt der Windows-Code?

WinForms/System.Drawing berührt 41 Dateien  davon:
  ├─ 33 reine UI-Dateien (Ui/-Ordner)          → müssen ohnehin neu
  └─  5 „Ausreißer" außerhalb der Ui-Ordner    → das ist die eigentliche Kopplung
       ├─ src/PolyTrader.Core/Modularity/ModuleUi.cs        (Func<Form>, Image)
       ├─ src/PolyTrader.Core/Modularity/WindowMenu.cs      (MenuStrip, Font)
       ├─ src/…CopyTrading/Logic/TradeRowColoring.cs        (Color)
       ├─ Models/DashboardRow.cs                            (using System.Drawing)
       └─ Program.cs                                        (ApplicationConfiguration.Initialize)

Nur 5 Dateien stehen zwischen „Core/Module sind plattformneutral" und dem Ist-Zustand.

1.3 Code-Verteilung

Kategorie LOC Anteil
UI (Designer + Code-Behind), App + Module ~8.300 34 %
davon Designer-generiert (entfällt ersatzlos) ~4.500 19 %
Services / Logik / Persistenz / Modelle ~15.950 66 %
Tests (448 Testfälle, 48 Dateien) ~5.300
EF-Migrationen (unverändert übernehmbar) 6.891

Nur 1 von 48 Testdateien ist UI-abhängig (WindowMenuTests.cs).


2. Die UI-Portierung

2.1 Control-Inventar und die neue Layout-Regel

16 Fenster/UserControls, ~8.300 LOC. Aus den Designer-Dateien:

WinForms-Control Anzahl Avalonia-Ersatz Bewertung
DataGridViewTextBoxColumn 69 DataGridTextColumn mechanisch, viel Fleißarbeit
Label 49 TextBlock trivial
ToolStripButton 37 Button in StackPanel Layout neu
TabPage / TabControl 22 / 7 TabItem / TabControl trivial
DataGridView 20 DataGrid siehe unten
ToolStrip 16 StackPanel/DockPanel Layout neu
Panel / Button 16 / 16 direkt trivial
TextBox / ComboBox 9 / 9 direkt trivial
PropertyGrid 6 handgebaute Formulare (Entscheidung 4) teuerster Punkt, siehe 2.2
DateTimePicker 5 CalendarDatePicker + TimePicker (getrennt!) kleine Nacharbeit
GroupBox 4 HeaderedContentControl/Border trivial
RichTextBox 3 kein Äquivalent, siehe 2.3 mittel
PictureBox 3 CartesianChart (LiveCharts2) siehe 2.4
SplitContainer 2 GridSplitter Layout neu
MenuStrip / StatusStrip 1 / 1 Menu / DockPanel ToolStripItemAlignment.Right gibt es nicht → DockPanel

Entwarnung bei den DataGrids: Die Nutzung ist erfreulich schlicht 25× DataSource-Binding, 35× DefaultCellStyle, 8× AutoGenerateColumns, nur 4 SelectionChanged, 3 CellContentClick, 1 CellDoubleClick. Kein Virtual Mode, kein Custom Painting, keine CellFormatting. Direkt auf ItemsSource + Binding StringFormat abbildbar.

Die Zeilenfärbung (ClosedTradesView.cs, OpenTradesView.cs über DataBindingCompleterow.DefaultCellStyle.BackColor) wird zu einem IValueConverter auf DataGrid.LoadingRow sauberer als heute.

📐 Die neue Layout-Regel (Entscheidung 2)

Die bisherige Vorgabe „alle WinForms-Controls designerfähig (partial + .Designer.cs), nie code-only" wird ersetzt durch:

„Jede View besteht aus View.axaml (vollständiges Layout, deklarativ) und View.axaml.cs (nur Verdrahtung und Datenlogik). Steuerelemente und Layout werden NICHT zur Laufzeit im Code erzeugt."

Nutzen bleibt identisch: Layout ist vollständig inspizierbar, diffbar und an einer Stelle. Ein WYSIWYG-Designer existiert in Avalonia nicht stattdessen der XAML-Previewer (VS-/Rider-Extension) mit Live-Vorschau beim Tippen.

Praktische Konsequenz für die Portierung: WindowMenu.Populate() baut die Menüleiste heute komplett zur Laufzeit auf (menu.Items.Clear() + dynamisch erzeugte ToolStripMenuItem). Das verstößt gegen die neue Regel und wird zu einem deklarativen Menu mit ItemsSource-Bindung an eine ObservableCollection<WindowMenuEntry> Layout im XAML, nur die Daten kommen aus dem Code. Gleiches gilt für die dynamisch angehängte Menüleiste in ShellUiHost.AttachWindowMenu().

2.2 PropertyGrid → normale Steuerelemente (Entscheidung 4)

Betroffen sind 4 Fenster mit 6 Grid-Instanzen:

Datei Instanz Gebundenes Objekt
Ui/Views/SettingsView.Designer.cs:21,33 propertyGrid, pgAccount ServerSettings, Account-Daten
…CopyTrading/Ui/AccountSettingsView.Designer.cs:25 pgSettings CopyTradingAccountSettings
…CopyTrading/Ui/MasterTradersView.Designer.cs:41 pgDetail TrackedTrader
…ResolutionFarming/Ui/ResolutionFarmingMainForm.Designer.cs:38 pgSettings Farming-Settings

Vorarbeit, die sich jetzt auszahlt: Die Modelle tragen bereits [Description]-Attribute als Inline-Erklärungen (CopyTradingAccountSettings.cs:11, TrackedTrader.cs:10) und [Browsable(false)] zum Ausblenden von Geheimnissen (ServerSettings.cs:107,146 Watchdog-Token und Lizenzschlüssel). Diese Attribute sind die Spezifikation der neuen Formulare Beschriftung, Tooltip und Sichtbarkeit jedes Feldes stehen schon im Code. Das reduziert die Denkarbeit auf Layout und Validierung.

Was die handgebauten Formulare besser können als das PropertyGrid:

  • Eingabevalidierung vor dem Speichern statt erst beim Commit der Zelle (bei Risiko-Limits und Preisgrenzen relevant)
  • Einheiten direkt am Feld (USDC, %, bps, Minuten) statt im Beschreibungstext
  • Fachliche Gruppierung statt alphabetischer Reflection-Reihenfolge
  • Abhängige Felder ausgrauen (z. B. Ladder-Parameter nur bei aktivierter Sell-Ladder)

Aufwand: ~810 PT statt ~2 PT mit Fremdbibliothek. Der Aufschlag ist bewusst und kauft dauerhafte Wartbarkeit ohne Fremdabhängigkeit.

2.3 RichTextBox → Terminal-Fenster

TerminalView (378 + 195 LOC) und SupervisorMainForm (Chat) nutzen RichTextBox mit Farbausgabe pro Log-Zeile. Avalonia hat kein RichTextBox.

Empfehlung: ItemsControl über eine ObservableCollection<LogLine> mit Farb-Converter virtualisiert, deklarativ (passt zur neuen Layout-Regel), und schneller als heute. TerminalView.cs:186 implementiert bereits ein Auto-Clear bei RAM-Limit; der Performance-Druck ist bekannt, ein virtualisiertes ItemsControl löst ihn an der Wurzel.

2.4 Charts: ScottPlot → LiveCharts2 (Entscheidung 8)

Nur 3 Charts, alle in DashboardView.RenderCharts() (Equity-Kurve, PnL je Modul, PnL je Tag). Die Auswertungslogik liegt bereits pur in TradeAnalytics und bleibt komplett unverändert.

Der heutige Weg (plot.GetImage(w,h).GetImageBytes()System.Drawing.BitmapPictureBox, DashboardView.cs:207-227) entfällt und wird zu drei deklarativen CartesianChart-Elementen im XAML was der neuen Layout-Regel ohnehin besser entspricht als das heutige Rendern im Code.

Zur Einordnung deiner Sorge: Der Unterschied ist kleiner als befürchtet. ScottPlot.Avalonia wäre ~1 PT günstiger gewesen, nicht mehr bei drei Charts fällt der Integrationsvorteil kaum ins Gewicht. LiveCharts2 bringt dafür Animationen, Tooltips und Zoom out of the box, die du bei ScottPlot nachbauen müsstest. Die Entscheidung für LiveCharts2 ist bei dieser Chart-Anzahl gut begründet du zahlst dafür keinen nennenswerten Aufpreis.

2.5 System.Drawing.Common muss raus

Seit .NET 7 ist System.Drawing.Common Windows-only und wirft auf Linux PlatformNotSupportedException. Betroffen:

  • ModuleUi.cs:32 System.Drawing.Image? IconAvalonia.Media.IImage
  • TradeRowColoring.cs:11-13 Color.FromArgb(...) → eigenes Farbtupel oder Avalonia.Media.Color
  • WindowMenu.cs Font, FontStyle.Bold
  • Models/DashboardRow.cs using System.Drawing (vermutlich verwaist, prüfen)
  • DashboardView.cs:222-225 entfällt mit LiveCharts2

TradeRowColoringTests.cs testet die Farben direkt → zieht mit (klein).

2.6 PDF-Export: PDFsharp-MigraDoc-GDI → Core-Build

Modules.Accounting referenziert PDFsharp-MigraDoc-GDI 6.2.4 der GDI+-Build ist Windows-only. Ersatz: PDFsharp-MigraDoc (Core-Build), API-kompatibel.

Aber: Der Core-Build findet auf Linux keine Systemschriften. PdfExporter.cs:34 setzt style.Font.Name = "Segoe UI" existiert auf Linux nicht. Nötig ist ein eigener IFontResolver plus eine als EmbeddedResource mitgelieferte Schrift (DejaVu Sans oder Open Sans, beide frei lizenziert). Einziger nennenswerter Umbau am Accounting-Modul.

AccountingReportTests.cs:131 rendert PDFs headless im Test → zieht mit.


3. Zeitzonen

3.1 Der harte Punkt

Ui/Views/TerminalView.cs:150:

TimeZoneInfo berlinTz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");

Eine Windows-Zeitzonen-ID. Seit .NET 6 mappt FindSystemTimeZoneById Windows-IDs auf Linux über ICU funktioniert also meistens. Es bricht, sobald InvariantGlobalization=true gesetzt ist oder die Distro kein vollständiges ICU mitbringt (Alpine ohne icu-libs). Dann fliegt beim Öffnen des Terminal-Fensters eine TimeZoneNotFoundException.

Mit Entscheidung 6 verschwindet diese Stelle ohnehin die Zeitzone kommt künftig aus der Konfiguration, nicht mehr hartcodiert.

3.2 Die Now/UtcNow-Mischung

122× DateTime.UtcNow gegen 23× DateTime.Now. Konsistent, solange der Rechner auf Europe/Berlin steht. Linux-Server laufen praktisch immer auf UTC. Betroffene Stellen:

Stelle Auswirkung bei Server-TZ = UTC
TerminalLogger.cs:23 Timestamp = DateTime.Now Log-Zeitstempel 12 h versetzt
TerminalLogger.cs:122, LauncherWidgetsPanel.cs:112 Logdatei {DateTime.Now:yyyy-MM-dd}.jsonl Tagesgrenze der Logdateien verschiebt sich
DossierService.cs:81, SupervisorTools.cs:144 suchen Logdateien per Datum müssen dieselbe Konvention nutzen, sonst leere Dossiers
DailyReportService.cs:61 Tagesbericht schneidet an anderer Stelle
AccountingMainForm.cs:57 Periodenvorbelegung steuerlich relevant Monats-/Quartalsgrenzen
PdfExporter.cs:55 „Erstellt: …" Belegdatum im Steuer-PDF
MasterTraderAnalyticsJob.cs mischt Now (Anzeige) und UtcNow (Cutoffs) funktioniert, ist aber verwirrend

3.3 Anforderung: konfigurierbare Zeitzone (Entscheidung 6)

Ziel (präzisiert): Die Zeitzone wird einmalig bei der Installation festgelegt — wie in einem Setup — und im laufenden Betrieb normalerweise nicht mehr gewechselt.

Vorgeschlagene Umsetzung:

  1. Neue Einstellung ServerSettings.ApplicationTimeZoneId (Default "Europe/Berlin"). IANA-IDs verwenden .NET akzeptiert sie seit Version 6 auf beiden Plattformen, Windows-IDs dagegen nur eingeschränkt auf Linux.

  2. Zentraler Zugriffspunkt statt verstreuter DateTime.Now-Aufrufe:

    IAppClock
      ├─ DateTime UtcNow          → für alle Domänen-/Persistenzwerte
      ├─ DateTime Local           → für Anzeige und Logdatei-Namen
      └─ TimeZoneInfo TimeZone    → aufgelöst aus ApplicationTimeZoneId, mit Fallback auf UTC + Warnung
    

    Damit ist die Zeitzone an einer Stelle wirksam statt an 23.

  3. Festlegung bei der Erstinstallation, danach schreibgeschützt-mit-Bestätigung:

    • Beim ersten Start ohne gesetzte Zeitzone wird sie abgefragt (UI-Dialog bzw. CLI-Schalter --set-timezone für den headless Betrieb) und in server_settings.xml festgeschrieben.
    • Eine spätere Änderung ist möglich, aber bewusst unbequem: Bestätigungsdialog mit Klartext-Folgen („Logdatei-Tagesgrenzen verschieben sich um X Stunden. Bereits geschriebene Logs und Buchungen bleiben unverändert und sind danach in der neuen Zeitzone zu lesen.") und Wirksamkeit erst nach Neustart.
    • Protokolleintrag mit alter und neuer Zeitzone, damit die Verschiebung später in den Logs nachvollziehbar ist.

    Weil der Regelfall die einmalige Festlegung ist, entfällt jede Komplexität für einen Wechsel zur Laufzeit — kein Neu-Verdrahten laufender Jobs, kein Umschalten offener Fenster. Das ist der eigentliche Gewinn dieser Entscheidung.

  4. Zusätzlich TZ-Umgebungsvariable in der systemd-Unit setzen, damit auch die Ausgaben außerhalb der Anwendung (Logrotate, Cron, Systemlogs) zusammenpassen.

Offene fachliche Frage: Soll die Zeitzone im Accounting mitwandern oder dort fix auf der steuerlich maßgeblichen Zeitzone stehen bleiben? Ein Wechsel des Betriebsstandorts ändert nicht zwingend den steuerlichen Bezugsrahmen. Empfehlung: zwei getrennte Einstellungen ApplicationTimeZoneId (Anzeige, Logs, Jobs) und AccountingTimeZoneId (Buchungsperioden, Steuerbelege), Default identisch. Kostet fast nichts, verhindert aber, dass ein Standortwechsel rückwirkend die Buchungsperioden verschiebt.

Aufwand: ~2 PT (Einstellung, IAppClock, 23 Aufrufstellen umziehen, Erstinstallations-Abfrage, Tests). Durch die Beschränkung auf die Installationszeit ~1 PT günstiger als in Revision 2 angesetzt.

3.4 MySQL

Pomelo speichert DateTime als DATETIME ohne Zeitzoneninformation. Bestehende Daten ändern sich beim Plattformwechsel nicht aber die Interpretation neuer Werte ändert sich. Prüfen: SELECT @@global.time_zone, @@session.time_zone; auf dem Ziel-Server. Bei gemischt geschriebenen Daten entstehen im Accounting stumme Lücken/Überlappungen an der Tagesgrenze. Der IAppClock aus 3.3 löst das, indem er die Persistenz konsequent auf UTC festlegt.


4. String- und Zahlenformatierung

4.1 ERLEDIGT der aktive Bug ist behoben

TraderMonitorService.cs parste API-Preise kulturabhängig. Empirisch verifiziert:

de-DE      "0.53" -> 53       "1234.5" -> 12345
Invariant  "0.53" -> 0,53     "1234.5" -> 1234,5

Ein Faktor-100-Fehler im Einstandspreis der Master-Trader-Auswertung.

Behoben am 06.08.2026:

  • TraderMonitorService.cs:1276-1280 nutzt jetzt den bereits vorhandenen, korrekten Helper ParseDecimal statt duplizierter Inline-Logik. Der Helper ist internal static und dokumentiert.
  • PolymarketClobClient.cs:770, 784, 796, 820, 822, 827 alle TryParse auf InvariantCulture (Regex-Gruppen sind zwar ziffernrein und waren damit faktisch sicher, aber die Klasse Fehler soll nirgends mehr im Code stehen).
  • MasterTraderAnalyticsJob.cs:140 Unix-Timestamp-String invariant.
  • Neuer Regressionstest tests/PolyTrader.Tests/ApiNumberParsingTests.cs 10 Fälle, die unter erzwungener de-DE-Kultur laufen und fehlschlagen, sobald jemand die Invariant-Angabe entfernt.

Verifikation: Build fehlerfrei, 448 Tests grün (438 vorher + 10 neue).

4.2 Ausgabeformatierung (offen)

35× DefaultCellStyle.Format, 19× ToString("…"), dazu zahlreiche :N2/:F2/:F3 in Log- und UI-Strings alle nach CultureInfo.CurrentCulture.

Auf Windows ist das de-DE. Auf Linux ist es LANG/LC_ALL unter systemd oft gar nicht gesetzt → .NET fällt auf Invariant zurück → aus 1.234,56 USDC wird 1,234.56 USDC. Kein Datenfehler, aber sofort sichtbar.

Empfehlung:

  1. Anzeige-Kultur beim Start explizit pinnen: CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE") in Program.Main. Damit ist die Darstellung unabhängig von LANG. (Sinnvollerweise ebenfalls konfigurierbar, analog zur Zeitzone dieselbe Begründung: Betrieb im Ausland.)
  2. Globalisierungs-Analyzer aktivieren (CA1304, CA1305, CA1307, CA1310). Sie hätten den Bug aus 4.1 gefunden.

4.3 Weitere Kulturthemen

  • ICU vs. NLS: .NET nutzt seit Version 5 auf beiden Plattformen ICU Sortier- und Vergleichssemantik bleibt gleich. Voraussetzung: kein InvariantGlobalization=true im Publish-Profil, libicu auf dem Zielsystem (Debian/Ubuntu Standard, Alpine nachinstallieren).
  • String-Vergleiche: durchgängig StringComparison.OrdinalIgnoreCase vorbildlich, keine Nacharbeit.

5. Weitere plattformspezifische Punkte

5.1 Dateisystem erfreulich sauber

Prüfpunkt Befund
Path.Combine 17 von 17 korrekt, durchgängig über AppContext.BaseDirectory
Environment.SpecialFolder 0 Treffer
Harte Windows-Pfade genau 1: ServerSettings.cs:73 MullvadCliPath (konfigurierbar, nur der Default ist Windows)
DllImport / LibraryImport 0 Treffer
Registry / WMI / DPAPI / EventLog 0 Treffer im eigenen Code

Groß-/Kleinschreibung: Linux ist case-sensitive. Kritisch: server_settings.xml, appsettings.Local.json, master.key, openrouter.key, Logs/, favicon.ico, die 24 PNGs in Resources/. Im Code konsistent trotzdem beim ersten Linux-Build gezielt prüfen, ebenso die \-Pfade in den .csproj (MSBuild normalisiert sie, aber es ist eine bekannte Stolperstelle).

Dateirechte: master.key und openrouter.key liegen als Klartext neben der Anwendung (SettingsView.cs:301,356, Program.cs:393, OpenRouterClient.cs:46). Auf Linux erzeugt File.WriteAllText mit Standard-umask 644 world-readable. Nach jedem Schreiben File.SetUnixFileMode(path, UserRead | UserWrite) (.NET 7+). Ergänzt Befund F1 des Sicherheitskonzepts.

Das Deploymentcenter macht es beim Lizenz-Cache bereits richtig (chmod 600 state.dat, siehe LICENSE_INTEGRATION_GUIDE Abschnitt 3) dieselbe Konvention für die eigenen Schlüsseldateien übernehmen.

Zeilenenden: Environment.NewLine wechselt von \r\n auf \n. CsvExporter.cs nutzt bereits explizites '\n' korrekt.

5.2 Lizenz & Watchdog jetzt über das Deploymentcenter (Entscheidung 5)

Das war in Revision 1 ein Blocker (R4) er ist gelöst. Aus J:\Softwareprojekte\Deploymentcenter\docs:

Thema Alt (LicenseLabrador.Client 1.0.0) Neu (Deploymentcenter, HWID v2)
Hardware-ID Windows: Registry MachineGuid; Linux: /etc/machine-idplattformabhängig verschieden 2:<plattform>:<sha256> mit dokumentierter Quellen-Priorisierung je Plattform
Container jede Instanz eine neue ID → Lizenz invalidiert bei jedem Neustart DEPLOYMENTCENTER_HWID-Override gewinnt plattformweit
Rechnername im Hash bewusst nicht enthalten, Umbenennen kostet keinen Aktivierungsplatz
Migration Client sendet hardware_id (v2) und legacy_hardware_id (v1); Server zieht die Aktivierung lautlos um, ohne zusätzlichen Platz
Headless modaler Dialog blockiert den Start CLI-Schalter --license-status, --license-set-key, --license-deactivate plus ILicensePrompt/ConsoleLicensePrompt als UI-Abstraktion
Cache LLS2-Envelope, AES-256-GCM, HKDF aus HWID+Slug, Linux 0600, kein Klartext-Rückfall
SDK netstandard2.0 Deploymentcenter.Client, Multi-Targeting netstandard2.0;net8.0

Damit entfallen zwei Risiken vollständig: die Neubindung der Lizenz beim Plattformwechsel (Migration v1→v2 ist serverseitig gelöst) und der Container-Neustart-Effekt.

Verbleibende Arbeit ein Umbau, kein Blocker:

  1. Lizenz: Licensing/LicenseGate.cs und Ui/LicenseDialog.cs von LicenseLabrador.Client auf Deploymentcenter.Client umstellen. Die API unterscheidet sich: LicenseResult/LicenseState/GetHardwareId()LicenseValidationResult (IsValid, Status, IsCached, Message) / HardwareId.GetHardwareId(slug) / ValidateAsync(slug, key, serverUrl). ILicensePrompt implementieren statt den Dialog direkt aufzurufen das ist genau die Naht, die den headless-Betrieb und später die Avalonia-UI trägt. Das lokale NuGet-Paket in lib/nuget/ wird ersetzt.
  2. Watchdog: WatchdogHeartbeatService sendet bereits source, instance, interval das Payload-Schema passt schon. Zu ändern sind nur Header (X-Watchdog-KeyX-Agent-Token) und Ziel-URL (watchdog.mhdf.dedc.mhdf.de/api/watchdog/v1/ping). Ergänzen: os- und group-Feld, sowie der im Guide empfohlene Abschluss-Ping beim geordneten Herunterfahren.

Aufwand: ~23 PT. Empfehlung: in Stufe L1 mitnehmen, nicht später der headless-Betrieb (L2) hängt an ILicensePrompt.

Sicherheitshinweis am Rande: server_settings.xml enthält den Watchdog-Token und den Lizenzschlüssel im Klartext (WatchdogToken, LicenseKey). Beim Umbau auf das Deploymentcenter ist der passende Moment, beide über die SecretProtection laufen zu lassen oder auf Umgebungsvariablen umzustellen passend zur bereits offenen Auflage, die Watchdog-Secrets zu rotieren.

5.3 Threema entfernt (Entscheidung 7)

Am 06.08.2026 umgesetzt. Nachfolgekanäle werden RocketChat und Telegram.

Was entfernt wurde:

Entfernt Umfang
src/PolyTrader.Core/Services/ThreemaService.cs 1 Datei (BackgroundService + Webhook-Listener)
libs/Threema-MsgApi-Net-Core (vendorte Fremdbibliothek) 35 Dateien, aus Solution und Git entfernt (History bleibt)
Projektreferenz in PolyTrader.Core.csproj 1
Threema-Block in ServerSettings (7 Properties, [Category("Threema Notifications")]) 36 Zeilen
DI-Registrierungen in Program.cs (Singleton + HostedService + View-Injektion) 3
ThreemaService-Verdrahtung in SettingsView 3 Stellen

Was an die Stelle getreten ist — INotificationSink:

PolyTrader.Core/Notifications/
  ├─ INotificationSink.cs    Schnittstelle + NotificationSeverity (Info/Warning/Critical)
  └─ LogNotificationSink.cs  Übergangs-Implementierung: schreibt ins Terminal-Log

Die drei Fachdienste kennen jetzt nur noch die Schnittstelle:

Dienst Meldung Severity
MasterTraderAnalyticsJob Auto-Pause eines Master-Traders Warning
SellLadderService SELL-Floor ohne Fill erreicht, Position wird gehalten Critical
DailyReportService täglicher Supervisor-Kurzbericht Info

Zwei Entwurfsentscheidungen, die sich später auszahlen:

  1. No-Throw-Vertrag. SendAsync wirft laut Schnittstellen-Doku niemals — Kanalfehler werden intern abgefangen und protokolliert. Das ist genau die Best-Practice aus dem Watchdog-Guide („Fange Netzwerkfehler stets stumm ab, damit der Ausfall des Monitoring-Servers niemals den Hauptanwendungsfluss unterbricht"). Die Aufrufer verzichten deshalb bewusst auf eigene try/catch-Blöcke — der Code wurde dabei kürzer, nicht länger. Bindend für die kommenden RocketChat- und Telegram-Implementierungen.
  2. NotificationSeverity ist von Anfang an dabei, obwohl die Log-Implementierung sie kaum nutzt. Bei zwei Kanälen (RocketChat für alles, Telegram als Fallback für Kritisches — oder umgekehrt) ist die Einstufung die natürliche Routing-Information. Sie nachträglich einzuziehen hätte alle Aufrufstellen erneut angefasst.

Nebeneffekte — vier Linux-Themen sind mit weggefallen:

Entfallen War
libsodium 1.0.16 (Baujahr 2018) Risiko R7: .so gegen alte glibc gelinkt, Ladefehler auf Ubuntu 24.04 möglich
Microsoft.Win32.Registry (E2EHelper.cs, #if CoreWinOnly) die einzige Registry-Nutzung im gesamten Build
HttpListener auf http://*:{port}/ (Webhook) Port-Bindung + netsh-Fehlermeldung mit Windows-Semantik
System.Web.HttpUtility

Verifikation: Clean-Build fehlerfrei, 448 Tests grün, Compiler-Warnungen von 17 auf 9 gesunken (die 8 Warnungen der Threema-Bibliothek — 4× SYSLIB0014, 3× CA1416, 1× CS0472 — sind weg).

Testverbesserung nebenbei: Die drei Tests, die bisher new ThreemaService(logger, new JobManager()) als Wegwerf-Instanz injizierten, nutzen jetzt FakeNotificationSink — damit ist prüfbar, ob und mit welcher Severity gemeldet wurde. Vorher war das nicht beobachtbar.

Offen für später: RocketChatNotificationSink und TelegramNotificationSink implementieren und in Program.cs registrieren — eine Klasse je Kanal, keine Änderung an der Fachlogik. Bei mehreren aktiven Kanälen bietet sich ein CompositeNotificationSink an (leitet an alle weiter, jeder Kanal fängt seine Fehler selbst ab).

5.4 Verbleibende Netzwerkdienste

  • HttpListener nach dem Threema-Ausbau nur noch der MCP-Light-Server (127.0.0.1:{port}/mcp). Läuft auf Linux über die managed Implementierung, kein netsh nötig. Unkritisch.
  • MullvadVpnService: Der CLI-Aufruf ist portabel (UseShellExecute = false, sauber). Auf Linux heißt die CLI mullvad (typisch /usr/bin/mullvad). Achtung: HealthCheckAsync() prüft status.Contains("Connected") der Wortlaut der Linux-CLI weicht ab. Muss gegen die echte CLI verifiziert werden, sonst reconnectet der Dienst dauerhaft ins Leere und meldet nach 3 Fehlversuchen „unrecoverable".
  • Nethereum 6.1.0, Pomelo/EF Core 8, HttpClient, ClientWebSocket, System.Text.Json, AES-GCM (OpenSSL): plattformneutral, keine Anpassung nötig. Das ist der gesamte Trading-Pfad.

5.5 Windows-UI als Fallback: Variante 2 (Entscheidung 3)

Heute laufen Generic Host und Application.Run im selben Prozess; darunter 19 BackgroundService-Instanzen. Auf Linux gibt es kein „WinExe" eine Avalonia-App braucht X11/Wayland, ein Trading-Server hat typischerweise kein Display.

Entschieden: Variante 2 eingefrorener Stand. Die WinForms-UI bleibt auf dem heutigen Stand und wird nicht weiterentwickelt; später verschwindet sie vollständig aus dem Build, damit keine Altlast mitgeschleppt wird.

Was das konkret heißt:

Einfrieren Vor Beginn von L3 einen Git-Tag setzen (Vorschlag: winforms-final) und im Repo dokumentieren. Ab da keine Feature-Arbeit mehr an Ui/ und den Ui/-Ordnern der Module.
Fallback im Ernstfall Tag auschecken, bauen, starten. Greift auf dieselbe MySQL zu — Anzeige und Einstellungen bleiben voll brauchbar.
Weiterentwicklung Läuft unverändert weiter — nur eben im Trading-Kern, der ab L2 headless auf Linux läuft. Eingefroren ist die Oberfläche, nicht die Funktion.
Endgültiger Ausbau Nach Abschluss von L4: Ui/-Ordner, PolyTrader.App als WinExe, alle .Designer.cs/.resx, UseWindowsForms, favicon.ico-Einbindung und ApplicationConfiguration.Initialize() entfernen. ~4.500 LOC generierter Designer-Code fallen ersatzlos weg.

Wichtig für die Reihenfolge: Damit der eingefrorene Stand überhaupt lauffähig bleibt, darf P1 die WinForms-UI nicht kaputtmachen. Beim Entkoppeln von Core und Modulen (Zielframework net8.0, System.Drawing raus) muss der WinForms-Zweig bis zum Tag baubar bleiben — praktisch heisst das: erst taggen, dann P1 durchziehen. Sonst ist der Fallback genau in dem Moment weg, in dem man ihn braucht.

Konkreter Vorschlag zur Absicherung: Vor P1 einmal dotnet publish -c Release des heutigen Stands erzeugen und das Ergebnis zusammen mit dem Tag archivieren. Ein fertig gebautes Verzeichnis ist im Ernstfall schneller einsatzbereit als ein Checkout, der erst wieder kompiliert werden muss — und beweist nebenbei, dass der eingefrorene Stand tatsächlich läuft.

Unabhängig davon nötig: headless-fähiger Einstiegspunkt für den Linux-Betrieb, systemd-Unit mit Restart=always und Environment=TZ=…, Master-Key über EnvironmentFile mit chmod 600, Logrotate für Logs/*.jsonl.

5.6 Tests und CI

  • Testprojekt auf net8.0 (fällt mit den Referenzen).
  • WindowMenuTests.cs einziger UI-Test, neu schreiben (~0,5 PT).
  • TradeRowColoringTests.cs zieht mit dem Farbtyp-Wechsel mit (~0,2 PT).
  • AccountingReportTests.cs:131 braucht den neuen FontResolver.
  • --smoke-ui (Program.cs:500-570): konstruiert alle Views headless und hat laut Kommentar bereits Designer-Regressionen gefangen. Unbedingt erhalten Avalonia bietet Avalonia.Headless, das Muster überträgt sich gut.
  • Es gibt keine CI. Die Portierung ist der richtige Anlass, dotnet build + dotnet test auf einem Linux-Runner einzurichten sonst schleicht sich die Windows-Abhängigkeit unbemerkt zurück.

6. Aufwandsschätzung (Revision 2)

# Paket Änderung ggü. Rev. 1 Aufwand
L0 Kultur-Bug erledigt 0,5 PT
P0 Zielbild, Avalonia-Spike an einer echten View, Layout-Konventionen festzurren 23 PT
P1 Core/Module von WinForms entkoppeln, TargetFramework auf net8.0, System.Drawing raus 23 PT
P2 Kultur & Zeit: IAppClock, Zeitzone bei Installation festlegen, Kultur pinnen, Analyzer 1 PT (Entscheidung 6 vereinfacht) 23 PT
P3 Plattform-Kleinkram: Mullvad-Pfad + Status-Parsing, Unix-Dateirechte 0,5 PT (Threema entfällt) 1 PT
P3b Threema-Ausbau + INotificationSink erledigt 06.08.2026 2 PT
P3c Lizenz/Watchdog auf Deploymentcenter umstellen neu (Entscheidung 5) 23 PT
P4 PDF-Export: PDFsharp Core + IFontResolver + eingebettete Schrift 12 PT
P5 Charts auf LiveCharts2 erledigt (0b8728b)
P6 UI-Shell: Launcher, Fenstermenü (deklarativ!), ShellUiHost, Shutdown-Dialog, Widgets erledigt (cd59e5c, 7f0b05e). Lizenzdialog bewusst ausgenommen → kommt mit P3c
P7 Core-Views: Dashboard, Settings, Jobs, Terminal erledigt (0b8728b, 4ba8149, 929fa20) PropertyGrid durch kategorisierten SettingsEditor ersetzt
P8 Modul-Fenster: CopyTrading, ResolutionFarming, Supervisor, Accounting erledigt (669676a, 7f0b05e)
P9 Tests (450 grün), --smoke-ui auf Avalonia.Headless, Linux-CI fehlt vollständig 23 PT
P10 Deployment: systemd, TZ , Logrotate, Feldtest im Zielland 23 PT
P11 WinForms endgültig ausbauen (nach L4): Ui/, Designer-Dateien, UseWindowsForms, WinExe 1 PT
P3c Deploymentcenter: Lizenzverwaltung, Watchdog, Error-Reporting neu ⬅️ als Nächstes 24 PT
Summe (offen) 713 PT

Personentage entwickelnder Arbeit. Mit Agentenunterstützung sinkt die Kalenderzeit deutlich; der Review- und Feldtest-Anteil (P9, P10) bleibt weitgehend gleich bei einer Anwendung, die echtes Geld bewegt, ist das die richtige Reihenfolge.


7. Fahrplan

Die zentrale Erkenntnis — eingetreten, dann überholt

Ursprünglich galt: P1P4 brauchen kein Avalonia, und mit einem headless-Einstiegspunkt hätte man für ~15 PT von rund 50 den eigentlichen Nutzen — Bot auf Linux, WinForms-UI als eingefrorener Fallback.

So ist es nicht gekommen, und das ist gut so. Die Portierung lief weiter, bis die Oberfläche vollständig unter Avalonia stand. Der Fallback wurde damit vom Dauerzustand zur reinen Rückversicherung im Tag winforms-final. Der aktuelle Engpass ist nicht mehr die UI, sondern die Betriebsreife: Deploymentcenter, CI, systemd und der Feldtest im Zielland.

Stufe Inhalt Aufwand Ergebnis
L0 Kultur-Bug erledigt Faktor-100-Preisfehler behoben, 10 Regressionstests
L0b Threema-Ausbau + INotificationSink erledigt 36 Dateien raus, 448 Tests grün, 8 Warnungen weniger
L1a Tag winforms-final + P1 (Core/Module entkoppeln) + P4 (PDF) erledigt Core, 4 Module und Testprojekt sind net8.0; alle sechs publishen für linux-x64 ohne Windows-Pakete; 442 Tests grün
L1b P0 + P2 + P3 (Mullvad, Dateirechte) + P3c (Deploymentcenter) 24 PT P0/P2 erledigt (Spike wurde zur vollen Portierung; Zeitzone konfigurierbar). Offen: P3c — steht als Nächstes an und ersetzt Lizenzverwaltung, Watchdog und Error-Reporting grundlegend.
L2 Headless-Einstiegspunkt + systemd + Feldtest im Zielland 24 PT --headless existiert (App.Avalonia/Program.cs:41). Offen: systemd-Unit + Feldtest. Trading läuft headless auf Linux. Meilenstein
L3 P5 + P6 + P7 (Charts, Shell, Core-Views) erledigt Avalonia-Grundgerüst (cd59e5c), Dashboard mit LiveCharts2 (0b8728b), Settings (4ba8149), Terminal (929fa20), Dunkelmodus (487ea44)
L4 P8 (Modul-Fenster) + P9 (CI) 24 PT Modul-Fenster portiert (669676a), Restlücken A1A4 geschlossen (7f0b05e), 450 Tests grün. Offen: es gibt keinerlei CI-Konfiguration weder .github/workflows noch .gitea/workflows.
L5 P11 WinForms endgültig aus dem Build entfernen 1 PT Erst nach Abnahme (A5). Keine Altlast mehr; ~4.500 LOC Designer-Code fallen weg, die 23 Build-Warnungen verschwinden mit

Reihenfolge-Begründung

  • Der Tag winforms-final kommt VOR P1. P1 entkoppelt Core und Module von WinForms — danach ist der alte Stand nicht mehr aus dem Hauptzweig baubar. Erst taggen (und einmal dotnet publish archivieren), dann umbauen. Sonst ist der Fallback genau dann weg, wenn man ihn braucht.
  • P3c (Deploymentcenter) gehört nach L1, nicht später der headless-Betrieb in L2 hängt an der ILicensePrompt-Abstraktion. Ohne sie blockiert der Lizenzdialog den Start auf einem Server ohne Display.
  • P2 vor L2 die Zeitzone muss feststehen, bevor der erste Linux-Server Logs und Buchungen schreibt. Nachträglich umzustellen bedeutet gemischte Daten an der Tagesgrenze.

Noch offene Punkte

  1. Accounting-Zeitzone getrennt von der Anwendungszeitzone? (3.3) Empfehlung ja, verhindert rückwirkend verschobene Buchungsperioden bei einem Standortwechsel.
  2. Anzeige-Kultur ebenfalls bei der Installation festlegbar? (4.2) naheliegend, da dieselbe Begründung wie bei der Zeitzone (Auslandsbetrieb).
  3. RocketChat und Telegram: Wann sollen die echten INotificationSink-Implementierungen kommen? Bis dahin landen Auto-Pause- und Eskalationsmeldungen nur im Terminal-Log. Für den unbeaufsichtigten Linux-Betrieb (L2) wäre mindestens ein echter Kanal wichtig — sonst bemerkst du eine Auto-Pause erst beim nächsten Blick ins Log.

8. Risikoliste (Revision 3)

# Risiko Status Wirkung / Gegenmaßnahme
R1 Kulturabhängiger Parse, Faktor-100-Preisfehler behoben gefixt + 10 Regressionstests unter de-DE
R4 Lizenz-Hardware-ID ändert sich, in Containern instabil gelöst Deploymentcenter HWID v2 mit DEPLOYMENTCENTER_HWID-Override und v1→v2-Migration ohne Platzverlust
R7 libsodium 1.0.16 gegen moderne glibc entfallen mit dem Threema-Ausbau am 06.08.2026
R2 Kein WYSIWYG-Designer; Layout-Regel ist neu offen Im Spike (P0) an einer echten View erproben, bevor 16 Views folgen
R3 PropertyGrid-Ersatz durch handgebaute Formulare offen Teuerster Posten (~10 PT). [Description]-Attribute liefern die Spezifikation
R5 Zeitzonen-Semantikwechsel trifft Logs, Tagesberichte, Accounting-Perioden offen P2 vor L2; IAppClock; Wirksamkeit erst nach Neustart
R6 Headless-Server ohne Display offen ILicensePrompt/ConsoleLicensePrompt aus dem Deploymentcenter-SDK; headless-Einstiegspunkt in L2
R8 Anzeigeformate kippen auf Invariant (LANG nicht gesetzt) offen Kultur in Program.Main pinnen
R9 Keine CI Windows-Abhängigkeiten schleichen zurück offen Linux-Build in Gitea Actions ab L1
R10 Mullvad-CLI meldet auf Linux anderen Statustext offen Gegen die echte CLI im Zielland verifizieren (5.4)
R11 Doppelpflege beider UIs entfällt Variante 2 gewählt (eingefrorener Stand)
R12 Benachrichtigungen fallen still entschärft INotificationSink eingezogen; Meldungen landen im Terminal-Log
R13 Kein echter Benachrichtigungskanal bis RocketChat/Telegram da sind offen Für den unbeaufsichtigten Betrieb (L2) mindestens einen Kanal implementieren — sonst bleibt eine Auto-Pause bis zum nächsten Log-Blick unbemerkt
R14 Fallback-Stand nicht mehr baubar, weil P1 vor dem Tag lief offen Tag winforms-final + archiviertes dotnet publish vor P1 (5.5)

9. Was nicht angepasst werden muss

  • Der gesamte Trading-Pfad: PolymarketClobClient (EIP-712 über Nethereum), PolymarketApiService, PolymarketWssClient, AlchemyWssClient, CopyTradingEngine, SellLadderService, TraderMonitorService
  • Persistenz: EF Core 8 + Pomelo/MySQL, alle 15 Migrationen, EncryptedStringConverter
  • Sicherheit: SecretProtection (AES-256-GCM läuft auf Linux über OpenSSL), Master-Key-Ableitung
  • Alle Analytics: TradeAnalytics, DossierBuilder, Supervisor-Agent, OpenRouter-Client, MCP-Light-Server
  • Accounting-Logik: Ingest, Klassifikation, BWA, CSV-Export (nur der PDF-Export braucht den FontResolver)
  • Watchdog-Payload-Schema (source/instance/interval passen bereits zur Deploymentcenter-API), Modularitäts-Infrastruktur
  • 438 von 448 Tests

Das ist der Grund, warum die Schätzung trotz 24.000 LOC damals bei 4161 PT landete und nicht bei 150: Zwei Drittel des Codes waren bereits plattformneutral, und die Trennung war sauber. Rückblickend hat sich das bestätigt — die Portierung lief schneller als veranschlagt.


10. Anhang: verifizierte Fundstellen

Behoben am 06.08.2026

Thema Datei:Zeile
Kulturabhängiger Preis-Parse (Faktor 100) …CopyTrading/Services/TraderMonitorService.cs:1276-1280 → nutzt jetzt ParseDecimal
ParseDecimal testbar + dokumentiert …CopyTrading/Services/TraderMonitorService.cs:1223-1234
Kulturabhängige Parses (Regex-Gruppen) …Core/Services/PolymarketClobClient.cs:770, 784, 796, 820, 822, 827
Kulturabhängiger Unix-Timestamp …CopyTrading/Services/MasterTraderAnalyticsJob.cs:140
Neuer Regressionstest (10 Fälle, de-DE) tests/PolyTrader.Tests/ApiNumberParsingTests.cs
Threema vollständig entfernt ThreemaService.cs, libs/Threema-MsgApi-Net-Core/ (35 Dateien), ServerSettings (7 Properties), Program.cs, SettingsView.cs, PolyTrader.Core.csproj, PolyTraderSharp.sln
Neutraler Benachrichtigungsausgang src/PolyTrader.Core/Notifications/INotificationSink.cs, LogNotificationSink.cs
Fachdienste auf INotificationSink umgestellt MasterTraderAnalyticsJob.cs, SellLadderService.cs, DailyReportService.cs
Testdouble mit Assertion-Möglichkeit tests/PolyTrader.Tests/Fakes/FakeNotificationSink.cs

Offen

Thema Datei:Zeile
Windows-Zeitzonen-ID hartcodiert Ui/Views/TerminalView.cs:150
Harter Windows-Pfad (Mullvad) src/PolyTrader.Core/Models/ServerSettings.cs:73
Mullvad-Statusprüfung (Wortlaut) src/PolyTrader.Core/Services/MullvadVpnService.cs:HealthCheckAsync
UI-Contract mit WinForms-Typen src/PolyTrader.Core/Modularity/ModuleUi.cs:32, 36
Menü wird zur Laufzeit gebaut (neue Layout-Regel) src/PolyTrader.Core/Modularity/WindowMenu.cs:Populate, Ui/ShellUiHost.cs:AttachWindowMenu
System.Drawing.Color in Modul-Logik …CopyTrading/Logic/TradeRowColoring.cs:11-13
ScottPlot → System.Drawing.Bitmap Ui/Views/DashboardView.cs:207-227
Windows-only Schrift im PDF …Accounting/Logic/PdfExporter.cs:34
Schlüsseldateien ohne Rechteschutz Ui/Views/SettingsView.cs:301, 356; Program.cs:393
Headless-Smoke-Test (erhaltenswert) Program.cs:500-570
Lizenz-Gate mit modalem Dialog Licensing/LicenseGate.cs, Program.cs:98
Secrets im Klartext server_settings.xml (WatchdogToken, LicenseKey)

Referenzen

  • J:\Softwareprojekte\Deploymentcenter\docs\LICENSE_INTEGRATION_GUIDE.md Hardware-ID v2, LLS2-Cache, CLI-Schalter, v1→v2-Migration
  • J:\Softwareprojekte\Deploymentcenter\docs\WATCHDOG_INTEGRATION_GUIDE.md Ping-API, X-Agent-Token, Payload-Schema
  • J:\Softwareprojekte\Deploymentcenter\client-dotnet\Deploymentcenter.Client SDK-Quellen (LicenseClient, HardwareId, ILicensePrompt, StateStore)