Phase 3: Trading-Kern (Risk, Execution, Portfolio) mit sicherem Broker-Default

- Core/Trading/TradingModels: Signal, Order(Request/Result), RiskContext/Decision,
  Account, Position, Quote, ExecutionResult, Enums (Side/OrderType/Mode)
- IBrokerClient + NullBrokerClient (sicherer Default, handelt NIE bis IBKR-Adapter verifiziert)
- RiskService (+IRiskService): Sizing nach MaxTrade%, Modul-Limit, Slippage; Buy/Sell
- PortfolioService (+IPortfolioService): core_position + core_trade_history + core_budget
- ExecutionService (+IExecutionService): Signal -> Kurs -> Konto -> Risiko -> Order -> Buchung
- TradingSettings in AppSettings (Paper/Live, TradingEnabled, Risikoparameter)
- CoreMigrations: core_position; DI-Registrierung der Trading-Services
- Tests: RiskService (11) + ExecutionService (6, NSubstitute) -> 38/38 gruen

Offen (bewusst gekapselt): echter IbkrBrokerClient gegen Client-Portal-Gateway (manuell verifizieren).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
This commit is contained in:
Richard
2026-07-27 11:33:59 +02:00
parent d0bc833235
commit 2ad4b55db1
15 changed files with 833 additions and 8 deletions
+48
View File
@@ -1,4 +1,5 @@
using System.ComponentModel;
using IBKRTrader.Core.Trading;
namespace IBKRTrader.Core.Settings;
@@ -171,6 +172,48 @@ public class WorkerSettings
public override string ToString() => "Worker-Konfiguration";
}
// ─── Trading ─────────────────────────────────────────────────────────────────
[TypeConverter(typeof(ExpandableObjectConverter))]
public class TradingSettings
{
[Category("Trading")]
[DisplayName("Modus")]
[Description("Handelsmodus: Paper (Test-Account, Port 4002) oder Live (Port 4001)")]
public string Mode { get; set; } = "Paper";
[Category("Trading")]
[DisplayName("Trading aktiv")]
[Description("Globaler Hauptschalter. Nur wenn aktiv werden Orders ausgeführt.")]
public bool TradingEnabled { get; set; } = false;
[Category("Trading")]
[DisplayName("Max. je Trade (%)")]
[Description("Maximaler Nominalwert einer einzelnen Position in % des Kontowerts")]
public double MaxTradePercent { get; set; } = 5.0;
[Category("Trading")]
[DisplayName("Max. je Modul (%)")]
[Description("Maximaler Gesamt-Nominalwert aller Positionen eines Moduls in % des Kontowerts")]
public double MaxPositionPercentPerModule { get; set; } = 20.0;
[Category("Trading")]
[DisplayName("Max. Slippage (%)")]
[Description("Maximal erlaubte Abweichung zwischen Limit-Preis und aktuellem Kurs")]
public double MaxSlippagePercent { get; set; } = 5.0;
[Category("Trading")]
[DisplayName("Gewinnziel (%)")]
[Description("Ziel-Gewinn einer Position in Prozent (für spätere Exit-Logik)")]
public double ProfitTargetPercent { get; set; } = 50.0;
/// <summary>Parst den Modus in das Enum (Fallback: Paper).</summary>
public TradingMode ParsedMode =>
Enum.TryParse<TradingMode>(Mode, true, out var m) ? m : TradingMode.Paper;
public override string ToString() => $"{Mode} {(TradingEnabled ? "aktiv" : "inaktiv")}";
}
// ─── Root ────────────────────────────────────────────────────────────────────
public class AppSettings
@@ -204,4 +247,9 @@ public class AppSettings
[DisplayName("Worker-Einstellungen")]
[Description("Zeitpläne der einzelnen Core-Worker")]
public WorkerSettings WorkerSettings { get; set; } = new();
[Category("Trading")]
[DisplayName("Trading")]
[Description("Handelsmodus und Risiko-Parameter")]
public TradingSettings Trading { get; set; } = new();
}