diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..53a8e6b --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,73 @@ +# Build- und Testlauf auf beiden Zielplattformen. +# +# Zweck: Die Portierung ist nur so lange etwas wert, wie sie nicht wieder zurückschleicht. Ein +# neues DateTime.Now, ein ToString("N2") ohne Formatanbieter oder eine WinForms-Referenz im Core +# fallen auf dem Windows-Entwicklungsrechner nicht auf – hier schon. +# +# Gitea Actions ist Actions-kompatibel; unter GitHub läuft dieselbe Datei als .github/workflows/. + +name: Build & Test + +on: + push: + branches: [main, 'feat/**'] + pull_request: + branches: [main] + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + + # Windows: die gesamte Projektmappe, inklusive der alten WinForms-Shell. + - name: Build (Windows, gesamte Mappe) + if: matrix.os == 'windows-latest' + run: | + dotnet build IBKRTrader.slnx -c Release + dotnet test IBKRTrader.slnx --no-build -c Release --logger "trx;LogFileName=test-results.trx" + + # Linux: alles AUSSER IBKRTrader.App (WinForms, net10.0-windows) – das lässt sich hier + # naturgemäß nicht bauen. Diese Aufzählung entfällt, sobald die WinForms-Shell aus dem + # Build genommen wird; dann baut auch hier wieder die ganze Mappe. + - name: Build (Linux, plattformneutrale Projekte) + if: matrix.os == 'ubuntu-latest' + run: | + dotnet build src/IBKRTrader.App.Avalonia -c Release + dotnet build src/IBKRTrader.Daemon -c Release + dotnet test tests/IBKRTrader.Tests -c Release --logger "trx;LogFileName=test-results.trx" + + # Die Konstruktionsprüfung der Oberfläche braucht mit Avalonia KEIN Anzeigegerät mehr + # (SetupWithoutStarting). Mit WinForms war das auf einem Build-Server nicht möglich. + - name: Smoke-UI (Fenster-Konstruktion) + run: dotnet run --project src/IBKRTrader.App.Avalonia --no-build -c Release -- --smoke-ui + + # Trockenlauf des kopflosen Dienstes: Host bauen, Startprüfungen fahren, nichts starten. + - name: Daemon-Prüflauf + run: dotnet run --project src/IBKRTrader.Daemon --no-build -c Release -- --check + + # Der eigentliche Portierungs-Wächter: läuft nur unter Linux und schlägt fehl, sobald ein + # Projekt wieder eine Windows-Abhängigkeit hereinzieht. + - name: Linux-Publish (Daemon + Oberfläche) + if: matrix.os == 'ubuntu-latest' + run: | + dotnet publish src/IBKRTrader.Daemon -c Release -r linux-x64 --self-contained false -o out/daemon + dotnet publish src/IBKRTrader.App.Avalonia -c Release -r linux-x64 --self-contained false -o out/gui + + - name: Testergebnisse sichern + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results-${{ matrix.os }} + path: '**/test-results.trx' + if-no-files-found: ignore diff --git a/src/IBKRTrader.App.Avalonia/Shell/ModuleViews.cs b/src/IBKRTrader.App.Avalonia/Shell/ModuleViews.cs index 9c139cf..b706b0e 100644 --- a/src/IBKRTrader.App.Avalonia/Shell/ModuleViews.cs +++ b/src/IBKRTrader.App.Avalonia/Shell/ModuleViews.cs @@ -1,4 +1,14 @@ +using IBKRTrader.App.Avalonia.Views.Modules; +using IBKRTrader.Core.Logging; using IBKRTrader.Core.Modularity; +using IBKRTrader.Core.Trading; +using IBKRTrader.Core.Workers; +using IBKRTrader.Modules.Accounting.Persistence; +using IBKRTrader.Modules.Accounting.Services; +using IBKRTrader.Modules.CongressTrading.Database; +using IBKRTrader.Modules.Supervisor.Agent; +using IBKRTrader.Modules.Supervisor.Persistence; +using IBKRTrader.Modules.Supervisor.Services; using Microsoft.Extensions.DependencyInjection; namespace IBKRTrader.App.Avalonia.Shell; @@ -11,20 +21,50 @@ namespace IBKRTrader.App.Avalonia.Shell; /// Modulprojekte bleiben deshalb frei von UI-Code; ihr RegisterUi ist leer, und die Shell /// verdrahtet die Fenster zentral. Die Modul-Dienste kommen unverändert aus dem DI-Container. /// -/// Stand: noch leer – die drei Modul-Fenster (CongressTrading, Supervisor, -/// Accounting) werden in der nächsten Etappe portiert. Bis dahin zeigt die Avalonia-Shell nur die -/// Core-Ansichten; die WinForms-Shell bleibt daneben vollständig nutzbar. +/// Registriert wird nur, was auch geladen ist: fehlt ein Modul in dieser Sitzung, entfällt +/// sein Fenster, und der Launcher zeigt es gar nicht erst an. /// public static class ModuleViews { public static void Register(IModuleUiHost host, IServiceProvider sp) { - // Noch keine portierten Modul-Fenster. Das Muster steht in der WinForms-Fassung - // (UI/ModuleViews.cs) und wird hier eins zu eins übernommen. + RegisterIfLoaded(sp, "CongressTrading", () => host.RegisterView(new ModuleView + { + Id = "congresstrading.main", Title = "Congress Trading", Group = "CongressTrading", Order = 100, + CreateView = () => new CongressTradingWindow( + host, + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService()) + })); + + RegisterIfLoaded(sp, "Supervisor", () => host.RegisterView(new ModuleView + { + Id = "supervisor.main", Title = "Supervisor", Group = "Supervisor", Order = 300, + CreateView = () => new SupervisorWindow( + host, + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService()) + })); + + RegisterIfLoaded(sp, "Accounting", () => host.RegisterView(new ModuleView + { + Id = "accounting.main", Title = "Accounting", Group = "Accounting", Order = 400, + CreateView = () => new AccountingWindow( + host, + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetRequiredService()) + })); } /// Registriert die Ansicht nur, wenn das Modul in dieser Sitzung geladen ist. - internal static void RegisterIfLoaded(IServiceProvider sp, string moduleName, Action register) + private static void RegisterIfLoaded(IServiceProvider sp, string moduleName, Action register) { if (sp.GetServices().Any(m => string.Equals(m.Name, moduleName, StringComparison.OrdinalIgnoreCase))) register(); diff --git a/src/IBKRTrader.App.Avalonia/ViewModels/Rows.cs b/src/IBKRTrader.App.Avalonia/ViewModels/Rows.cs index 6574320..7edc5ec 100644 --- a/src/IBKRTrader.App.Avalonia/ViewModels/Rows.cs +++ b/src/IBKRTrader.App.Avalonia/ViewModels/Rows.cs @@ -16,3 +16,30 @@ public sealed record ModuleRow(string Name, string Prefix, string Status); /// über SelectionColor der RichTextBox ein, in Avalonia wird je Element gebunden. /// public sealed record LogRow(string Text, global::Avalonia.Media.IBrush Color); + +/// Eine offene Position im Modul-Fenster. +public sealed record PositionRow(string Symbol, int Quantity, decimal AvgPrice, decimal Notional); + +/// Eine Zeile der Supervisor-Berichtsliste. Tokens als fertiger Text (Prompt / Completion). +public sealed record SupervisorReportRow( + DateTime CreatedAt, string Profile, string Model, string Question, int ToolCallCount, string Tokens); + +// ─── Accounting ────────────────────────────────────────────────────────────── +// Beträge als bereits formatierter Text: die Umrechnung in die Anzeigewährung passiert beim +// Laden, und das Format ist damit an einer Stelle festgelegt statt in jeder Spaltendefinition. + +/// Eine Monatszeile des Monatsvergleichs (BWA). +public sealed record MonthlyRow( + string Month, string Opening, string Deposits, string Withdrawals, + string Volume, string Fees, string Result, string Closing); + +/// Eine Buchung im neutralen Ledger. +public sealed record LedgerRow( + DateTime Time, string AccountId, string EventType, string Side, string Symbol, + string Currency, decimal Quantity, decimal Price, + decimal Gross, decimal Fee, decimal Net, string TransactionId); + +/// Ein Ingest-Lauf des Flex-Query-Abrufs. +public sealed record IngestRunRow( + string AccountId, DateTime Started, DateTime? Finished, bool Backfill, + int NewEntries, int DuplicateEntries, bool Success, string Delta, string Message); diff --git a/src/IBKRTrader.App.Avalonia/Views/Modules/AccountingWindow.axaml b/src/IBKRTrader.App.Avalonia/Views/Modules/AccountingWindow.axaml new file mode 100644 index 0000000..31f30be --- /dev/null +++ b/src/IBKRTrader.App.Avalonia/Views/Modules/AccountingWindow.axaml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + +