# Analyse: Linux-Fähigkeit des IBKRTrader
> Stand: 2026-08-06. **Reine Analyse – es wurde kein Code geändert.**
> Grundlage ist der Commit `b96a207` (main): 154 C#-Dateien, ~16.200 LOC, 6 Projekte, 165 Tests.
> Alle Aussagen in Abschnitt 1–9 sind am Quelltext bzw. an einem Probe-Restore verifiziert;
> Stellen, die nur plausibel und ungeprüft sind, stehen ausdrücklich als solche gekennzeichnet.
---
## 0. Kurzfassung
**Die gute Nachricht:** Der portabilitätskritische Teil der Anwendung ist bereits sauber. Kein
einziges `DllImport`, keine Registry, kein WMI, kein DPAPI, kein `TimeZoneInfo.FindSystemTimeZoneById`.
Die Verschlüsselung (`AesGcm`), die Datenbank (Pomelo/EF Core) und der TWS-Adapter laufen ohne
Änderung auf Linux. Die WinForms-Kopplung im **Core** beschränkt sich auf **drei Dateien**, und
jedes Modul trägt **genau eine** Form-Datei.
**Die eigentliche Arbeit** liegt an zwei Stellen und sie sind unterschiedlich groß:
| | Umfang | Aufwand |
|---|---|---|
| **A. Headless-Linux** (Worker, Trading, Accounting, Supervisor, REST/MCP – ohne UI) | ~15 Fundstellen, 3 Core-Dateien entkoppeln | **5–7 Personentage** |
| **B. Desktop-Linux** (zusätzlich die komplette UI auf Avalonia) | ~1.300 LOC WinForms neu bauen | **+10–14 Personentage** |
| **C. LiveCharts2** (heute existiert **kein einziges** Diagramm) | Neubau, keine Migration | **+1–3 Personentage** |
**Empfehlung:** Die beiden Schritte trennen. Eine headless Linux-Variante ist mit ~1 Woche
erreichbar und liefert sofort den größten praktischen Nutzen (Dauerbetrieb auf einem Server statt
auf dem Windows-Desktop). Der Avalonia-Umbau ist danach eine unabhängige Etappe, die man ohne
Zeitdruck und ohne laufenden Betrieb zu gefährden angehen kann. Details in Abschnitt 11.
**Ein Punkt ist unabhängig von der UI der gefährlichste:** die Zeitzonen-Behandlung (Abschnitt 6).
Der Code mischt heute `DateTime.Now` und `DateTime.UtcNow` und verwirft in `ParseExecutionTime`
bewusst die Zeitzone. Auf einem Windows-Rechner mit `Europe/Berlin` und in einem Linux-Container
mit `UTC` liefert **derselbe Code unterschiedliche Werte** – lautlos, ohne Fehler, in
Buchungszeitstempeln. Das muss vor der Portierung geklärt werden, nicht danach.
---
## 1. Was **nicht** geändert werden muss
Diese Prüfungen sind negativ ausgefallen – das sind ersparte Personentage:
| Geprüft | Ergebnis |
|---|---|
| `DllImport` / `LibraryImport` / `Marshal` | **keine Fundstelle** |
| Windows Registry, WMI, EventLog, `WindowsIdentity` | **keine Fundstelle** |
| DPAPI / `ProtectedData` | **keine Fundstelle** – `SecretProtection` nutzt `AesGcm` + `SHA256`, voll portabel |
| `TimeZoneInfo.FindSystemTimeZoneById` (Windows- vs. IANA-IDs) | **keine Fundstelle** |
| Hartkodierte Laufwerksbuchstaben im Produktivpfad | nur in `BackupWorker` (s. 7.1) |
| EF Core / Pomelo / MySqlConnector | voll portabel, Migrationen unberührt |
| WinForms-Designer-Dateien (`*.Designer.cs`, Layout-`.resx`) | **keine** – die gesamte UI ist handgeschriebener Code-Behind. Das erspart die übliche Designer-Konvertierung vollständig. |
### 1.1 Die TWS-API läuft auf Linux (geprüft)
Das Paket `IB.TWS.CSharpApi 9.76.1` liefert `lib/net45/CSharpAPI.dll` – deshalb steht heute
`NoWarn="NU1701"` im Core-csproj. Die Assembly-Referenzen wurden ausgelesen:
```
mscorlib 4.0.0.0
System 4.0.0.0
System.Core 4.0.0.0
```
Nur diese drei, alle auf .NET 10 vollständig typweitergeleitet. Es gibt keine Abhängigkeit auf
`System.Configuration`, `System.Web` oder sonst etwas Windows-Gebundenes. Die DLL wird auf Linux
laufen. **Verbleibendes Restrisiko: gering, aber ungeprüft** – ein Verbindungs-Smoke-Test gegen
das Gateway von einem Linux-Host aus gehört in die erste Etappe.
> **Sauberere Alternative:** IBKR liefert im offiziellen TWS-API-Download den C#-Quelltext mit.
> Den als eigenes `netstandard2.0`-Projekt in `src/` aufzunehmen, ersetzt das NuGet-Mirror-Paket,
> beseitigt `NU1701` und macht die Herkunft nachvollziehbar. Halber Tag, optional.
---
## 2. Sofort-Blocker: der Restore schlägt heute auf **jedem** frischen Rechner fehl
Das ist keine Linux-Besonderheit, aber es ist das Erste, worüber man auf einer neuen Maschine
stolpert – und deshalb Teil dieser Analyse. `NuGet.config` nutzt `packageSourceMapping` als
Allowlist mit ``. Drei Pakete haben kein passendes Muster. Verifiziert mit einem Restore
gegen einen leeren Paket-Ordner:
```
error NU1100: "PDFsharp-MigraDoc (>= 6.2.4)" kann für "net10.0-windows" nicht aufgelöst werden.
error NU1100: "Microsoft.EntityFrameworkCore (>= 8.0.13)" kann für "net10.0-windows" nicht aufgelöst werden.
error NU1100: "Microsoft.CodeAnalysis.CSharp.Workspaces (>= 4.5.0)" kann für "net10.0-windows" nicht aufgelöst werden.
```
Auf dem Entwicklungsrechner fällt das nicht auf, weil alle drei längst im globalen Paket-Cache
liegen. Die Ursachen:
- `PDFsharp-MigraDoc` – gar kein Muster vorhanden.
- `Microsoft.EntityFrameworkCore` – das Muster lautet `Microsoft.EntityFrameworkCore.*`; der Glob
matcht das Paket **ohne** Suffix nicht.
- `Microsoft.CodeAnalysis.CSharp.Workspaces` – transitiv über `EntityFrameworkCore.Design`, kein Muster.
**Aufwand: 10 Minuten.** Muss vor allem anderen erledigt sein, sonst startet der erste
Linux-Build nicht.
---
## 3. Block A – Projekt- und Build-Ebene
**Alle sechs Projekte** stehen heute auf `net10.0-windows` mit `UseWindowsForms=true` – auch der
Core, alle drei Module und das Testprojekt.
| Projekt | heute | Ziel |
|---|---|---|
| `IBKRTrader.Core` | `net10.0-windows`, WinForms | `net10.0`, **keine** UI-Abhängigkeit |
| `Modules.CongressTrading` | `net10.0-windows`, WinForms | `net10.0` |
| `Modules.Accounting` | `net10.0-windows`, WinForms | `net10.0` |
| `Modules.Supervisor` | `net10.0-windows`, WinForms | `net10.0` |
| `IBKRTrader.Tests` | `net10.0-windows`, WinForms | `net10.0` |
| `IBKRTrader.App` | `WinExe`, `net10.0-windows` | neu: `IBKRTrader.Desktop` (Avalonia) + `IBKRTrader.Daemon` (Konsole) |
Zwei Nebenwirkungen, die man kennen muss:
1. **`ImplicitUsings` + `UseWindowsForms` fügt `System.Windows.Forms` und `System.Drawing` als
globale Usings hinzu.** Sobald das wegfällt, brechen Dateien, die unbemerkt `Point`, `Size`,
`Color`, `Font` oder `Padding` benutzt haben. Das ist *nützlich* – der Compiler findet die
Arbeit für uns – aber es erklärt, warum die erste Umstellung mehr Fehler wirft als die 3
bekannten Core-Dateien vermuten lassen.
2. `ApplicationHighDpiMode`, `Properties/Resources.resx` (24 `System.Drawing.Bitmap`-Icons) und
`ApplicationConfiguration.Initialize()` verschwinden mit dem App-Projekt. Die 24 PNGs unter
`Resources/` bleiben brauchbar, werden aber in Avalonia über `AvaloniaResource` + `Bitmap`
eingebunden statt über den `ResourceManager`.
**Aufwand Block A: 0,5 Tage.**
---
## 4. Block B – den Core von WinForms lösen
Das ist der architektonisch wichtigste Schritt und erfreulich klein. **Genau drei Dateien** im
Core kennen WinForms:
### 4.1 `Core/Logging/LoggingService.cs` (139 LOC, davon ~40 betroffen)
Hält direkt ein `RichTextBox?`, benutzt `System.Drawing.Color` und marshallt selbst per
`InvokeRequired`/`BeginInvoke`:
```csharp
private RichTextBox? _rtb;
public void AttachRichTextBox(RichTextBox rtb) => _rtb = rtb;
private static readonly Color ColorInfo = Color.FromArgb(150, 210, 150);
```
**Umbau:** `AttachRichTextBox` durch ein `event Action? EntryWritten` bzw. ein
`ILogSink`-Interface ersetzen. Das Einfärben und das Thread-Marshalling wandern in die UI-Schicht
(Avalonia: `Dispatcher.UIThread.Post`). Der `LogEntry`-Record ist bereits sauber und braucht
keine Änderung.
Nebenbei zu bereinigen: `e.Level.ToString().ToUpper()` in Zeile 115 ist kulturabhängig
(s. Abschnitt 5).
### 4.2 `Core/Modularity/ModuleView.cs` (57 LOC)
```csharp
public System.Drawing.Image? Icon { get; set; }
public Func