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> @
256 lines
9.9 KiB
C#
256 lines
9.9 KiB
C#
using System.ComponentModel;
|
||
using IBKRTrader.Core.Trading;
|
||
|
||
namespace IBKRTrader.Core.Settings;
|
||
|
||
// ─── Database ────────────────────────────────────────────────────────────────
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public class DatabaseSettings
|
||
{
|
||
[Category("Database")]
|
||
[DisplayName("Host")]
|
||
[Description("MySQL-Serveradresse (Hostname oder IP)")]
|
||
public string Host { get; set; } = "larx.your-database.de";
|
||
|
||
[Category("Database")]
|
||
[DisplayName("Port")]
|
||
[Description("MySQL-Port (Standard: 3306)")]
|
||
public int Port { get; set; } = 3306;
|
||
|
||
[Category("Database")]
|
||
[DisplayName("Database")]
|
||
[Description("Name der MySQL-Datenbank")]
|
||
public string Database { get; set; } = "bergisnu_db0";
|
||
|
||
[Category("Database")]
|
||
[DisplayName("User")]
|
||
[Description("MySQL-Benutzername")]
|
||
public string User { get; set; } = "bergisnu_0";
|
||
|
||
[Category("Database")]
|
||
[DisplayName("Password")]
|
||
[Description("MySQL-Passwort")]
|
||
[PasswordPropertyText(true)]
|
||
public string Password { get; set; } = "o5/A49+f}:Da";
|
||
|
||
public string BuildConnectionString() =>
|
||
$"Server={Host};Port={Port};Database={Database};" +
|
||
$"User={User};Password={Password};" +
|
||
$"AllowPublicKeyRetrieval=true;SslMode=Preferred;CharSet=utf8mb4;" +
|
||
$"ConnectionTimeout=15;DefaultCommandTimeout=30;";
|
||
|
||
public override string ToString() => $"{User}@{Host}:{Port}/{Database}";
|
||
}
|
||
|
||
// ─── IBKR TWS/Gateway (Socket API) ───────────────────────────────────────────
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public class IBKRSettings
|
||
{
|
||
[Category("IBKR Gateway")]
|
||
[DisplayName("Host")]
|
||
[Description("Adresse des IBKR Gateway (Standard: 127.0.0.1)")]
|
||
public string Host { get; set; } = "127.0.0.1";
|
||
|
||
[Category("IBKR Gateway")]
|
||
[DisplayName("Port")]
|
||
[Description("IBKR Gateway Port (Paper: 4002, Live: 4001)")]
|
||
public int Port { get; set; } = 4001;
|
||
|
||
[Category("IBKR Gateway")]
|
||
[DisplayName("Client ID")]
|
||
[Description("Eindeutige Client-ID für die API-Verbindung")]
|
||
public int ClientId { get; set; } = 1;
|
||
|
||
public override string ToString() => $"{Host}:{Port} (Client {ClientId})";
|
||
}
|
||
|
||
// ─── IBKR Web API (Client Portal Gateway) ────────────────────────────────────
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public class IBKRWebApiSettings
|
||
{
|
||
[Category("IBKR Web API")]
|
||
[DisplayName("Aktiviert")]
|
||
[Description("Aktiviert die IBKR Web API Marktdaten-Worker")]
|
||
public bool Enabled { get; set; } = false;
|
||
|
||
[Category("IBKR Web API")]
|
||
[DisplayName("Gateway URL")]
|
||
[Description("URL des Client Portal Gateway (Standard: https://localhost:5000)")]
|
||
public string GatewayUrl { get; set; } = "https://localhost:5000";
|
||
|
||
[Category("IBKR Web API")]
|
||
[DisplayName("History-Zeitraum")]
|
||
[Description("Zeitraum für den ersten historischen Import (z.B. 2Y, 5Y, 1Y)")]
|
||
public string HistoryPeriod { get; set; } = "2Y";
|
||
|
||
[Category("IBKR Web API")]
|
||
[DisplayName("Max Requests/Sek")]
|
||
[Description("Maximale API-Anfragen pro Sekunde (IBKR-Limit: 10, empfohlen: 5)")]
|
||
public int MaxRequestsPerSecond { get; set; } = 5;
|
||
|
||
public override string ToString() => Enabled ? $"Aktiv – {GatewayUrl}" : "Deaktiviert";
|
||
}
|
||
|
||
// ─── Logging ─────────────────────────────────────────────────────────────────
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public class LoggingSettings
|
||
{
|
||
[Category("Logging")]
|
||
[DisplayName("Log Level")]
|
||
[Description("Minimales Log-Level: Info, Warn oder Error")]
|
||
public string Level { get; set; } = "Info";
|
||
|
||
[Category("Logging")]
|
||
[DisplayName("Max. Dateigröße (MB)")]
|
||
[Description("Maximale Größe einer Log-Datei in Megabyte")]
|
||
public int MaxFileSizeMb { get; set; } = 10;
|
||
|
||
[Category("Logging")]
|
||
[DisplayName("Aufbewahrung (Tage)")]
|
||
[Description("Anzahl Tage, nach denen Log-Dateien gelöscht werden")]
|
||
public int RetentionDays { get; set; } = 30;
|
||
|
||
public override string ToString() => $"Level: {Level}";
|
||
}
|
||
|
||
// ─── Webserver ───────────────────────────────────────────────────────────────
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public class WebserverSettings
|
||
{
|
||
[Category("Webserver")]
|
||
[DisplayName("Port")]
|
||
[Description("Port des internen Webservers (Web-UI)")]
|
||
public int Port { get; set; } = 5001;
|
||
|
||
[Category("Webserver")]
|
||
[DisplayName("Aktiviert")]
|
||
[Description("Webserver und REST-API beim Start aktivieren")]
|
||
public bool Enabled { get; set; } = false;
|
||
|
||
public override string ToString() => Enabled ? $"Aktiv auf Port {Port}" : "Deaktiviert";
|
||
}
|
||
|
||
// ─── Workers ─────────────────────────────────────────────────────────────────
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public class WorkerSchedule
|
||
{
|
||
[Category("Worker")]
|
||
[DisplayName("Aktiviert")]
|
||
public bool Enabled { get; set; } = true;
|
||
|
||
[Category("Worker")]
|
||
[DisplayName("Interval (Minuten)")]
|
||
public int IntervalMinutes { get; set; } = 30;
|
||
|
||
public override string ToString() => Enabled ? $"alle {IntervalMinutes} min" : "Deaktiviert";
|
||
}
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public class WorkerSettings
|
||
{
|
||
[Category("Workers")]
|
||
[DisplayName("BackupWorker")]
|
||
[Description("Zeitplan für den automatischen Backup-Worker")]
|
||
public WorkerSchedule BackupWorker { get; set; } = new();
|
||
|
||
[Category("Workers")]
|
||
[DisplayName("IBKR Instrument-Sync")]
|
||
[Description("Zeitplan für die IBKR Stammdaten-Synchronisation (alle 30 Tage)")]
|
||
public WorkerSchedule InstrumentSyncWorker { get; set; } = new() { IntervalMinutes = 43200 }; // 30 Tage
|
||
|
||
[Category("Workers")]
|
||
[DisplayName("IBKR Preishistorie")]
|
||
[Description("Zeitplan für den täglichen IBKR Preishistorie-Abruf")]
|
||
public WorkerSchedule PriceHistoryWorker { get; set; } = new() { IntervalMinutes = 1440 }; // 24h
|
||
|
||
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
|
||
{
|
||
[Category("Database")]
|
||
[DisplayName("Datenbank")]
|
||
[Description("MySQL-Verbindungseinstellungen")]
|
||
public DatabaseSettings Database { get; set; } = new();
|
||
|
||
[Category("IBKR Gateway")]
|
||
[DisplayName("IBKR")]
|
||
[Description("Interactive Brokers Gateway-Verbindung (Socket API)")]
|
||
public IBKRSettings IBKR { get; set; } = new();
|
||
|
||
[Category("IBKR Web API")]
|
||
[DisplayName("IBKR Web API")]
|
||
[Description("Client Portal Gateway für Marktdaten")]
|
||
public IBKRWebApiSettings IBKRWebApi { get; set; } = new();
|
||
|
||
[Category("Logging")]
|
||
[DisplayName("Logging")]
|
||
[Description("Log-Level und Datei-Einstellungen")]
|
||
public LoggingSettings Logging { get; set; } = new();
|
||
|
||
[Category("Webserver")]
|
||
[DisplayName("Webserver")]
|
||
[Description("Interner HTTP-Server und REST-API")]
|
||
public WebserverSettings Webserver { get; set; } = new();
|
||
|
||
[Category("Workers")]
|
||
[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();
|
||
}
|