R8: Accounting- + Supervisor-Modul + Core-Datenfundament (S-0)

Portierung der beiden fehlenden Grundbausteine aus PolytraderSharp (voller Ausbau).

Core S-0 (Datenfundament fuer Analyse/Forensik):
- core_decision_journal + core_order_events (+ ReasonCode/Decision/OrderEvent-Enums),
  IDecisionJournal/IOrderEventLog mit fehlertoleranten EF-Impls (Handel bricht nie).
- SignalId-Durchreichung TradeSignal -> ExecutionService -> core_trade_history;
  ExecutionService schreibt an jeder Verzweigung Journal/Order-Events.
- JSONL-Log-Sink (LogJson + Dual-Sink), pure Analytik: RealizedPnlEngine (FIFO),
  TradeAnalytics, DossierBuilder. Migration AddAnalysisFoundation.

Accounting-Modul (acc_): unabhaengiger IBKR-Kontoauszug (Activity Flex Query) hinter
Interfaces mit Offline-Null-Stubs -> append-only Ledger + Periodenabrechnung/BWA + FX
(USD/EUR) + CSV/PDF (PDFsharp/MigraDoc). Steuerschicht bewusst offen (Platzhalter-Tab).
Kein Handel. Migration InitialAccounting.

Supervisor-Modul (sup_): read-only OpenRouter-Agent (Function-Calling-Loop) + read-only
Tool-Registry (8 Tools) + Profile + Dossier-Browser + Counterfactual-Job (Stub) +
Tagesbericht/MCP-Light (opt-in). Migration InitialSupervisor.

Verdrahtung: Program.cs (beide Module + Icons), slnx/App/Tests-Referenzen,
provision-db.ps1, AppSettings-Sektionen, docs/konzepte, README.

Tests: 79 -> 117 gruen (FIFO/KPIs/Dossier/JSONL, Classifier/Engine/FX/Idempotenz,
OpenRouter/Registry/Agent/MCP, STA-Konstruktion beider neuen Fenster).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-31 09:25:18 +02:00
co-authored by Claude Opus 4.8
parent cbbedb2e0e
commit 2a312ca035
86 changed files with 6880 additions and 22 deletions
@@ -0,0 +1,87 @@
using FluentAssertions;
using IBKRTrader.Core.Logging;
using IBKRTrader.Core.Persistence;
using IBKRTrader.Core.Persistence.Ef;
using IBKRTrader.Modules.Accounting.Persistence;
using IBKRTrader.Modules.Accounting.Services;
using IBKRTrader.Modules.Accounting.Ui;
using IBKRTrader.Modules.Supervisor.Agent;
using IBKRTrader.Modules.Supervisor.Persistence;
using IBKRTrader.Modules.Supervisor.Services;
using IBKRTrader.Modules.Supervisor.Ui;
using Microsoft.EntityFrameworkCore;
namespace IBKRTrader.Tests;
/// <summary>
/// Konstruiert die neuen Modul-Fenster mit In-Memory-/Stub-Abhängigkeiten gleichwertig zum
/// Headless-Smoke-UI-Check (`--smoke-ui`), aber ohne die laufende App/DB. Forms bauen im Konstruktor
/// nur Controls (DB-Zugriff erst auf Interaktion), daher genügt Instanziierbarkeit der Services.
/// </summary>
[Trait("cat", "unit")]
public class UiConstructionTests
{
private sealed class Factory<T>(DbContextOptions<T> options) : IDbContextFactory<T> where T : DbContext
{
public T CreateDbContext() => (T)Activator.CreateInstance(typeof(T), options)!;
}
private static Factory<T> InMemory<T>() where T : DbContext =>
new(new DbContextOptionsBuilder<T>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options);
private sealed class NoChat : IChatCompletionClient
{
public Task<ChatResponse> CompleteAsync(string m, IReadOnlyList<ChatMessage> msgs,
IReadOnlyList<SupervisorTool> tools, CancellationToken ct) => Task.FromResult(new ChatResponse());
}
private static Exception? ConstructOnSta(Action action)
{
Exception? captured = null;
var t = new Thread(() => { try { action(); } catch (Exception ex) { captured = ex; } });
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
return captured;
}
[Fact]
public void AccountingMainForm_Constructs()
{
var ex = ConstructOnSta(() =>
{
var logger = new LoggingService();
var accDbf = InMemory<AccountingDbContext>();
var ledger = new EfLedgerRepository(accDbf);
var runs = new EfIngestRunRepository(accDbf);
var report = new AccountingReportService(ledger, new EfFxRateRepository(accDbf));
var ingest = new AccountingIngestService(
new NullAccountSource(), ledger, runs, new EfRawSnapshotRepository(accDbf),
new NullStatementSource(), new NullBalanceAnchorSource(), logger);
using var form = new AccountingMainForm(ledger, runs, report, ingest, logger);
form.Text.Should().Be("Accounting");
});
ex.Should().BeNull();
}
[Fact]
public void SupervisorMainForm_Constructs()
{
var ex = ConstructOnSta(() =>
{
var logger = new LoggingService();
var coreDbf = InMemory<CoreDbContext>();
var supDbf = InMemory<SupervisorDbContext>();
IDecisionJournal journal = new EfDecisionJournal(coreDbf, logger);
IOrderEventLog orderLog = new EfOrderEventLog(coreDbf, logger);
var dossiers = new DossierService(journal, orderLog, new TradeLogReader(coreDbf));
var agent = new SupervisorAgent(new NoChat(), new SupervisorToolRegistry());
var reports = new EfSupervisorReportRepository(supDbf, logger);
using var form = new SupervisorMainForm(agent, dossiers, reports, logger);
form.Text.Should().Be("Supervisor");
});
ex.Should().BeNull();
}
}