Files
IBKRTrader/docs/ARCHITECTURE.md
RichardandClaude Opus 5 c176b05ea1
Build & Test / build (ubuntu-latest) (push) Waiting to run
Build & Test / build (windows-latest) (push) Waiting to run
L6: IBKRTrader.App.Avalonia -> IBKRTrader.App; letzte WinForms-Spuren raus
Das Suffix ".Avalonia" gab es nur, weil daneben ein WinForms-IBKRTrader.App
stand. Das ist seit L5 weg, also faellt auch das Suffix. Git erkennt alle
Dateien als Umbenennung; Assembly, Wurzel-Namensraum und die
avares://-Ressourcen-URI sind mitgezogen.

Nebeneffekt der Umbenennung: die global::Avalonia-Qualifizierungen entfallen.
Sie waren noetig, weil der Namensraum IBKRTrader.App.Avalonia das
Avalonia-Paket verdeckt hat - ein Ueberbleibsel genau der Namensgebung, die
jetzt weg ist.

Inhaltlich falsch gewordene Aussagen berichtigt - das waren die eigentlichen
Ueberbleibsel, nicht die Kommentare:
- .agents/rules/grundregeln.md schrieb weiterhin "C# .NET 10 WinForms",
  RichTextBox-Logging, LauncherForm und PropertyGrid vor. Das ist die Regel,
  nach der kuenftig gearbeitet wird - sie haette die Portierung Stueck fuer
  Stueck rueckgaengig gemacht. Jetzt: Avalonia, keine Plattform-Suffixe, die
  11er-Pinnung mit Begruendung, dazu die beiden Regeln, die uns in L1b am
  meisten gekostet haben (UTC persistieren + AppTimeZone statt DateTime.Now;
  jede Formatierung mit ausdruecklichem IFormatProvider).
- Core: LogEntry ("wird in RichTextBox geschrieben"), IWorker/WorkerEngine/
  WorkerInfo ("DataGridView-Zeile"/"-Binding"), ModuleView ("die
  WinForms-Shell castet auf Form").
- Doku: ARCHITECTURE (Modul-Ui-Ordner, "designbare Forms mit Initialize"),
  KONZEPT-Modul-Accounting ("UI (WinForms, ein Fenster mit Tabs)").

BEWUSST STEHEN GEBLIEBEN sind die Kommentare, die WinForms nur als
Begruendung nennen - warum LoggingService ein Ereignis hat statt einer
RichTextBox, warum ModuleView Func<object> liefert, warum es benannte
Record-Zeilentypen gibt, warum die Einstellungsmaske aus Attributen entsteht.
Das ist die Herleitung des heutigen Entwurfs; ohne sie sieht spaeter jede
dieser Stellen nach Umstaendlichkeit ohne Grund aus.

KONZEPT-Linux-Portierung.md bekommt einen Statusvermerk: umgesetzt, die
Pfadangaben im Fundstellenverzeichnis beziehen sich auf den alten Aufbau.
Zwei Abweichungen von der Schaetzung sind dort festgehalten - der geringere
Aufwand dank der PolytraderSharp-Vorlage, und dass die dort empfohlene
InvariantGlobalization ein Fehler gewesen waere.

Verifiziert: Build 0 Fehler/0 Warnungen, 193 Tests gruen, Smoke-UI
konstruiert alle 7 Ansichten + Launcher + Dialog, Daemon-Prueflauf OK,
publish -r linux-x64 fuer beide Einstiegspunkte fehlerfrei.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 21:16:48 +02:00

180 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IBKRTrader Architektur & Implementierungsplan
> **KORRIGIERT (2026-07-27):** Vorbild ist **PolytraderSharp** (`J:\Softwareprojekte\PolytraderSharp`),
> die C#-Neuentwicklung **nicht** die veraltete Python-Version. Die frühere Fassung dieses Plans
> basierte auf der Python-Vorlage und war konzeptionell falsch. Ziel ist eine **1:1-Neu-Fundamentierung**
> nach PolytraderSharp, im bestehenden IBKRTrader-Repo.
Ziel: modulares C#-Trading-Framework für Interactive-Brokers-Aktien, strukturell wie PolytraderSharp,
nur dass statt Polymarket über IBKR gehandelt wird.
---
## 1. Ziel-Architektur (nach PolytraderSharp)
> **Stand seit der Linux-Portierung (2026-08-07):** Alle Projekte sind `net10.0` ohne
> Plattformbindung. Die WinForms-Shell ist entfernt; der letzte Stand liegt im Tag
> `winforms-final`. Einstiegspunkte sind jetzt `IBKRTrader.App` (mit Oberfläche) und
> `IBKRTrader.Daemon` (kopflos, systemd); beide bauen ihren Host über `IBKRTrader.Hosting`.
> Analyse und Vorgehen: [konzepte/KONZEPT-Linux-Portierung.md](konzepte/KONZEPT-Linux-Portierung.md).
```
src/IBKRTrader.App (WinExe, net10.0) Oberfläche: Launcher, Shell, Core-Views, Modul-Fenster
│ Program.cs: Host bauen (Hosting), Startprüfungen, dann Avalonia; --smoke-ui ohne Anzeigegerät
│ Shell/: AvaloniaUiHost, CoreViews, ModuleViews, ViewIcons, WindowMenu
│ Views/: LauncherWindow, Dashboard/Workers/Logs/Settings, Views/Modules/ (3 Modul-Fenster)
src/IBKRTrader.Daemon (Exe, net10.0) kopfloser Dienst: --check, --db-version, SIGTERM
src/IBKRTrader.Hosting (classlib, net10.0) AppHostBuilder + RunStartupChecks, von beiden geteilt
├── src/IBKRTrader.Core (classlib, net10.0 plattformneutral, kein UI-Toolkit)
│ ├── Modularity/ IModule (Name, DbPrefix, RegisterServices, RegisterUi, Start/Stop, ActivationBlocker)
│ │ ModuleView, IModuleUiHost ← toolkit-neutraler UI-Contract (Func<object>, IconKey)
│ ├── Time/ AppTimeZone (Betriebszeitzone der Instanz; Persistenz bleibt UTC)
│ ├── Configuration/ DatabaseOptions, ServerVersion-Pinning
│ ├── DependencyInjection/ AddCorePersistence(...)
│ ├── Persistence/Ef/ CoreDbContext + Entities + EF-Repositories (hinter Interfaces)
│ ├── Trading/ Broker-Seam, Risk, Execution, Portfolio, Domänentypen
│ ├── Services/ TerminalLogger, JobManager, Hosted Services (Market-Sync etc.)
│ ├── Security/ SecretProtection (Master-Key, AES-256-GCM at-rest)
│ └── Hosting/ StartupHydrationService (IHostedService, hydriert State zuerst)
├── src/IBKRTrader.Modules.CongressTrading (classlib, referenziert NUR Core)
│ ├── CongressTradingModule : IModule
│ ├── Persistence/Ef/ eigener DbContext (ct_) + Repos
│ ├── Services/ Scraper + Jobs (IHostedService)
│ └── (kein UI-Code das Modul-Fenster liegt in der Shell, s. App/Shell/ModuleViews.cs)
└── tests/IBKRTrader.Tests (xUnit, referenziert Core + Module)
```
### Leitprinzipien (aus PolytraderSharp übernommen)
- **Multi-Projekt**: Core = eigenes Assembly; jedes Modul = eigenes Projekt, referenziert **nur** Core.
Module können einander physisch nicht referenzieren.
- **Generic Host**: `Host.CreateDefaultBuilder`, `IHostedService` für alle Hintergrund-Jobs,
`IOptions`/`IConfiguration`.
- **Config**: `appsettings.json` + `appsettings.Local.json` (gitignored, hält Connection-String/Secrets).
- **Modul-Vertrag** `IModule`: `Name`, `DbPrefix`, `RegisterServices(services, config)`,
`RegisterUi(host, sp)`, `StartAsync/StopAsync`, `GetActivationBlocker(config)`.
- **UI = Shell + Views**: Der Core stellt den toolkit-neutralen Contract (`ModuleView`,
`IModuleUiHost`), die Shell setzt ihn in Avalonia um. Der Launcher öffnet je View ein Fenster
(Einzelinstanz, erneutes Öffnen fokussiert), jedes Fenster trägt das gemeinsame „Fenster"-Menü.
Layout deklarativ in `.axaml`; Module tragen keinen UI-Code.
- **Persistenz**: EF Core (Pomelo/MySQL), `AddDbContextFactory`, Repositories hinter Interfaces.
- **Sicherheit**: Master-Key + AES-256-GCM-Verschlüsselung von Credentials at-rest; TLS-Warnung.
- **Headless-Test**: `--smoke-ui` konstruiert jede View + Launcher ohne Message-Loop.
- **Module an/aus** über `ServerSettings.DisabledModules`.
---
## 2. Was aus dem bisherigen Stand übernommen wird
| Bestand (Phase 03, Python-basiert) | Schicksal |
|---|---|
| Testprojekt (xUnit) | bleibt, wandert nach `tests/` |
| `RiskService`, `ExecutionService`, Domänentypen | Logik bleibt → Core/Trading (Feinschliff) |
| CongressTrading Scraper/Repo | Inhalt bleibt → eigenes Modul-Projekt (auf EF/IHostedService umgestellt) |
| `IModule`/`ModuleRegistry`/`WindowManager` | ersetzt durch PolytraderSharp-Contract (`IModule` neu, `ModuleView`, `IModuleUiHost`, `WindowMenu`) |
| Manuelles `ServiceCollection` in Program.cs | ersetzt durch Generic Host |
| `settings.json`/`SettingsService` | ersetzt durch `IConfiguration` + `appsettings.Local.json` |
| Dapper + manuelle Migrationen | ersetzt durch EF Core |
| `WorkerEngine`/`IWorker` | ersetzt durch `IHostedService` |
---
## 3. Re-Fundamentierungs-Phasen (Checkliste)
### R1 Solution-Skelett (Multi-Projekt, reiner Strukturumbau, Verhalten unverändert) ✅
- [x] `src/IBKRTrader.Core` (classlib, UseWindowsForms) `Core/` + UI-Contract (`Ui/ModuleFormBase`, `Ui/WindowManager`)
- [x] `src/IBKRTrader.Modules.CongressTrading` (classlib, referenziert nur Core)
- [x] Root-Projekt → `IBKRTrader.App` (WinExe), referenziert Core + Modul; Program/LauncherForm/UI-Shell bleiben
- [x] `tests/IBKRTrader.Tests` Referenzen auf Core + Modul; Fixture-Pfad angepasst
- [x] Neue `.slnx`; ungenutztes HtmlAgilityPack entfernt; Build + **38/38 Tests grün**; App startet
### R2 Core-Contracts + Generic Host + Shell-UI ✅
- [x] `IModule` (PolytraderSharp-Stil) + `ModuleView` + `IModuleUiHost` + `WindowMenu` in Core
- [x] `Program.cs``Host.CreateDefaultBuilder`; `IConfiguration` (appsettings.json + appsettings.Local.json, gitignored)
- [x] `ShellUiHost` + `LauncherForm` (View-Buttons + gemeinsames Fenster-Menü); Core-Views (Workers/Logs/Settings) als eigene Fenster
- [x] CongressTrading auf neuen `IModule`-Vertrag; Modul-Worker als `IWorker` registriert
- [x] `--smoke-ui` Headless-Test (alle Views + Launcher konstruieren) → grün
- [ ] **Offen (R4/R5):** Worker von `WorkerEngine`/`IWorker` auf `IHostedService` umstellen (aktuell noch WorkerEngine)
### R3 Persistenz auf EF Core
**Festgelegt:** EF-Migrationen **extern** (wie PolytraderSharp) Schema per `dotnet ef database update`,
App legt keine Tabellen zur Laufzeit an. Server: **MariaDB 11.8.6** (via `--db-version` bestätigt) →
Pin `new MariaDbServerVersion(new Version(11, 8, 6))`. Verbindung aus `appsettings.Local.json`.
- [x] `--db-version`-Diagnose (Serverversion für den EF-Pin)
- [x] **Slice 1:** `Configuration/DatabaseOptions` + `DatabaseServerVersion`-Pin (MariaDB 11.8.6); `AddCorePersistence` (`AddDbContextFactory`) + `CoreDbContext` + Entities (core_position, core_trade_history, core_budget, core_worker_log, core_settings) + Design-Time-Factory; **EF-Migration `InitialCore` erzeugt**
- [x] **Slice 2:** `PortfolioService`, `BudgetService`, `TradeHistoryService` auf EF (`IDbContextFactory<CoreDbContext>`) umgestellt; **5 EF-InMemory-Unit-Tests** (Buchführung real verifiziert). WorkerBase-Log folgt in Slice 4.
- [x] **Slice 3:** Modul auf EF (`CongressTradingDbContext` ct_ + `CongressRepository`); `WorkerBase`-Log auf EF (core_worker_log); `CoreSettingsService` (core_settings); `CoreMigrations`/`CongressMigrations` (Dapper) entfernt; EF-Migration `InitialCongressTrading`; `appsettings.Local.json` (gitignored) als Connection-Quelle; Fallback für leeren Connection-String. **+4 EF-InMemory-Tests** (CongressRepository)
- [x] **Slice 4:** IBKR-Marktdaten (`core_ibkr_*`) auf EF (3 Entities im `CoreDbContext`, Cross-Modul-Query via Raw-SQL); Migration `AddIbkr`; **Dapper + `DatabaseService` + `IBKRMigrations` vollständig entfernt** Persistenz ist jetzt **komplett EF Core**. +4 EF-InMemory-Tests.
**R3 abgeschlossen** gesamte Persistenz auf EF Core (Migrationen: `InitialCore`, `AddIbkr`, `InitialCongressTrading`), extern via `dotnet ef database update` anzuwenden.
- Hinweis: nur build-verifizierbar (Unit-Tests ohne DB); Schema-Anwendung extern via `dotnet ef database update` (env `IBKRTRADER_MYSQL`)
### R4 Worker auf `IHostedService` ✅
- [x] `WorkerBase` implementiert `IHostedService`; `IWorker` auf Metadaten+Trigger reduziert
- [x] Worker via `AddHostedService` registriert; Lebenszyklus über `AppHost.Start()`/`StopAsync()`
- [x] `WorkerEngine` auf leichte Registry reduziert (WorkerInfos für UI + `TriggerWorkerAsync`)
- [x] `LauncherForm` startet/stoppt keine Worker mehr; Build + 43/43 Tests + smoke-ui + App-Start grün
- Hinweis: Trading-Kern (Risk/Execution/Portfolio/Broker) wurde bereits in Phase 3 gebaut und in R3 auf EF gehoben
### R5 CongressTrading als vollständige Strategie ✅
- [x] `CongressTradingStrategy`: neuer Scrape-Trade → `TradeSignal` (buy/sell-Mapping) → Core-`IExecutionService`
- [x] `CongressScrapeWorker` ruft die Strategie je neuem Trade auf (per try/catch isoliert)
- [x] Modul-Fenster zeigt offene Positionen (`IPortfolioService.GetPositionsAsync("CT")`)
- [x] **8 neue Tests** (Signal-Mapping/Ausführung, gemockter ExecutionService) → 51/51 grün
- Hinweis: Handel bleibt durch Trading-Gate (`TradingEnabled=false`) + `NullBrokerClient` sicher aus, bis echter Broker + Freigabe
### R6 Sicherheit + Config-Härtung ✅
- [x] `SecretProtection` (Master-Key aus env `IBKRTRADER_MASTER_KEY`/`master.key`, AES-256-GCM at-rest, selbstheilendes `enc:v1:`-Format) + `EncryptedStringConverter` (bereit für künftige IBKR-Credentials)
- [x] `ConfigureSecretProtection` + TLS-Warnung (`SslMode`) beim Start; `master.key` gitignored
- [x] Connection-String in `appsettings.Local.json` (gitignored)
- [x] **5 SecretProtection-Tests** (Round-Trip, Idempotenz, Passthrough, Tamper/Key-Fehler) → 56/56 grün
- [ ] **Offen (Nutzer-Aktion):** geleaktes DB-Passwort rotieren (liegt in Git-Historie via `grundregeln.md`, Commit `ebeb035`); EF-Schema per `dotnet ef database update` auf die DB anwenden
### R7 Feinschliff ✅
- [x] Core-**Dashboard-View** (Gesamtüberblick: Trading-Modus, aggregierte Kennzahlen, geladene Module) + Icon
- [x] `DashboardService` (aggregiert Positionen/Exposure/Trades via EF) + **2 InMemory-Tests** → 58/58 grün
- [x] Tests durchgehend portiert; `--smoke-ui` deckt alle Views ab
- [x] **Echter `IbkrBrokerClient` über die TWS API** (2026-07-31): `src/IBKRTrader.Core/Trading/Ibkr/` `IbkrMapping` (rein, unit-getestet), `IbkrConnection` (Socket + reqId-Korrelation), `IbkrBrokerClient` (`IBrokerClient`). Aktivierung über `IBKRSettings.UseTwsApi`; `NullBrokerClient` bleibt Default. Design und Grenzen: [IBKR-Integration.md](IBKR-Integration.md), Einstellungen: [TWS-Setup-Checkliste.md](TWS-Setup-Checkliste.md).
- [x] Gegen Paper-Konto DUR371528 verifiziert: Verbindung, Konto (NetLiquidation 100.105,50 EUR), Kurse (AAPL/MSFT/NVDA, verzögert) und Fehlerpfade
- [x] Orderpfad bis zur Broker-Annahme per **What-If-Order** verifiziert (Aktie + Option, keine Ausführung); **Optionsberechtigung im Paper-Konto bestätigt**
- [x] **`IBrokerPortfolioReader`** (Bestand + Ausführungen beim Broker) eigener Seam neben `IBrokerClient`, Grundlage für den Abgleich der eigenen Buchführung; gegen DUR371528 verifiziert (2 Positionen, 2 Ausführungen inkl. Kommissionen)
- [ ] **Offen:** `PlaceOrderAsync` mit echter Ausführung verifizieren (Fill → Buchung); asynchrone Fill-Verfolgung (Orders ohne sofortige Ausführung)
- [ ] IBKR-Account-Credentials mit `EncryptedStringConverter` speichern
### L0L5 Linux-Portierung: Avalonia statt WinForms ✅ (2026-08-07)
Analyse und Begründung: [konzepte/KONZEPT-Linux-Portierung.md](konzepte/KONZEPT-Linux-Portierung.md).
Rückfallpunkt für den letzten WinForms-Stand: Tag `winforms-final`.
- [x] **L0** `NuGet.config` repariert drei Pakete hatten kein `packageSourceMapping`-Muster; ein frischer Klon konnte nicht wiederherstellen (auf dem Entwicklungsrechner unsichtbar, weil gecacht)
- [x] **L1a** Core und Module von WinForms entkoppelt: `net10.0` statt `net10.0-windows`. UI-Contract toolkit-neutral (`Func<object> CreateView`, `IconKey` statt `System.Drawing.Image` letzteres ist seit .NET 7 Windows-only). `LoggingService` meldet über `EntryWritten` statt eine `RichTextBox` zu halten
- [x] **L1b** **Betriebszeitzone** (`AppTimeZone`, `Trading.ApplicationTimeZoneId`): EU- und US-Instanzen sauber getrennt, Persistenz bleibt UTC. `ParseExecutionTime` verwirft die von TWS gemeldete Börsenzeitzone nicht mehr, sondern rechnet gegen sie nach UTC. Kultur-Fixes (PDF-Beträge fest `de-DE`, Scraper-Datum `TryParseExact`), `BackupWorker` plattformunabhängig, DB-Passwort über `MYSQL_PWD` statt Kommandozeile
- [x] **L2** Kopfloser Dienst `IBKRTrader.Daemon` (systemd, `--check`, `--db-version`) + `IBKRTrader.Hosting` als geteilte Host-Zusammenstellung. `AppPaths` (Umgebungsvariable → Binärverzeichnis wenn beschreibbar → FHS)
- [x] **L3** Avalonia-Shell (11.3.19, DataGrid 11.3.13) + Core-Ansichten. `PropertyGrid` ersetzt durch eine aus den Attributen erzeugte Einstellungsmaske (11 Abschnitte, 41 Felder)
- [x] **L4** Die drei Modul-Fenster portiert; CI-Matrix ubuntu + windows
- [x] **L5** WinForms vollständig entfernt `IBKRTrader.App`, `LauncherForm`, `UI/`, `Properties/Resources.*`
**Ergebnis:** Alle Projekte `net10.0` ohne Plattformbindung. `publish -r linux-x64` liefert Daemon (11 MB)
und Oberfläche (32 MB) ohne eine einzige Windows-Abhängigkeit. Der Smoke-UI-Lauf braucht kein
Anzeigegerät mehr und ist damit erstmals Teil der CI.
**Offen:** IB Gateway kopflos betreiben (IBC + Xvfb) eigene Baustelle, unabhängig vom Code;
LiveCharts2 kommt mit den neuen Modulen (Avalonia deshalb auf der 11er-Linie gepinnt).
---
## Kurskorrektur abgeschlossen (R1R7)
IBKRTrader entspricht jetzt strukturell dem PolytraderSharp-Konzept: Multi-Projekt (Core + Modul + App + Tests),
Generic Host + `IHostedService`, `IConfiguration`, `IModule`/`ModuleView`/`ShellUiHost`, EF Core (extern migriert),
Trading-Kern (Risk/Execution/Portfolio, `NullBroker`-Default), CongressTrading-Strategie, Security (Master-Key/AES-GCM).
**Offen für später:** echte IBKR-Broker-Anbindung (Paper-Gateway), DB-Passwort-Rotation, EF-Schema anwenden.
---
## 4. Historie (bereits erledigt, teils zu ersetzen)
- Phase 03 (Python-basiert): Testfundament, IModule/ModuleRegistry, LauncherForm+WindowManager,
Trading-Kern (Risk/Execution/Portfolio, NullBroker). Logik brauchbar, Infrastruktur wird ersetzt.
- Commits bis `2ad4b55` auf Gitea.