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; } = "your-db-host";
[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; } = "your-db-name";
[Category("Database")]
[DisplayName("User")]
[Description("MySQL-Benutzername")]
public string User { get; set; } = "your-db-user";
[Category("Database")]
[DisplayName("Password")]
[Description("MySQL-Passwort")]
[PasswordPropertyText(true)]
public string Password { get; set; } = "CHANGE_ME";
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;
[Category("IBKR Gateway")]
[DisplayName("TWS-Broker verwenden")]
[Description("Aktiviert den echten TWS-Broker. Aus = NullBroker (handelt nie). " +
"Der globale Handelsschalter unter Trading bleibt davon unberührt.")]
public bool UseTwsApi { get; set; } = false;
[Category("IBKR Gateway")]
[DisplayName("Marktdaten-Typ")]
[Description("1 = Realtime, 2 = Frozen, 3 = verzögert, 4 = verzögert+Frozen. " +
"Paper-Konten ohne Datenabo brauchen 3 oder 4, sonst kommen keine Kurse.")]
public int MarketDataType { get; set; } = 4;
[Category("IBKR Gateway")]
[DisplayName("Verbindungs-Timeout (s)")]
[Description("Wartezeit auf Socket-Verbindung und Handshake. TWS braucht nach einem Neustart mehrere Minuten.")]
public int ConnectTimeoutSeconds { get; set; } = 15;
[Category("IBKR Gateway")]
[DisplayName("Anfrage-Timeout (s)")]
[Description("Wartezeit auf Kurs-, Kontrakt- und Kontoantworten")]
public int RequestTimeoutSeconds { get; set; } = 15;
[Category("IBKR Gateway")]
[DisplayName("Order-Timeout (s)")]
[Description("Wartezeit auf den Endstatus einer Order. Danach gilt sie als fehlgeschlagen, " +
"kann bei IBKR aber weiterhin aktiv sein.")]
public int OrderTimeoutSeconds { get; set; } = 60;
public override string ToString() =>
$"{Host}:{Port} (Client {ClientId}){(UseTwsApi ? "" : " – TWS-Broker aus")}";
}
// ─── 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("Betriebszeitzone")]
[Description("Zeitzone dieser Instanz in IANA-Schreibweise, z. B. \"Europe/Berlin\" (EU) oder " +
"\"America/New_York\" (US). Gilt für Anzeige, Logdatei-Tagesgrenzen, Berichtszeiten " +
"und Buchungsperioden; gespeichert wird immer UTC. VOR den ersten Trades festlegen " +
"und danach nicht mehr ändern – ein Wechsel verschiebt rückwirkend alle Tagesgrenzen. " +
"Leer = Zeitzone des Systems (nicht empfohlen). Wirkt erst nach einem Neustart.")]
public string ApplicationTimeZoneId { get; set; } = "Europe/Berlin";
[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;
/// Parst den Modus in das Enum (Fallback: Paper).
public TradingMode ParsedMode =>
Enum.TryParse(Mode, true, out var m) ? m : TradingMode.Paper;
public override string ToString() => $"{Mode} – {(TradingEnabled ? "aktiv" : "inaktiv")}";
}
// ─── Accounting ──────────────────────────────────────────────────────────────
[TypeConverter(typeof(ExpandableObjectConverter))]
public class AccountingSettings
{
[Category("Accounting")]
[DisplayName("Basiswährung")]
[Description("Kontobasiswährung für die Abrechnung (Standard: USD)")]
public string BaseCurrency { get; set; } = "USD";
[Category("Accounting")]
[DisplayName("Ingest-Intervall (Stunden)")]
[Description("Abstand der automatischen Kontoauszug-Abrufe (Live-Quelle; offline ungenutzt)")]
public int IngestIntervalHours { get; set; } = 6;
[Category("Accounting")]
[DisplayName("Flex Query-Id")]
[Description("IBKR Activity Flex Query-Id (Zielland – Live-Abruf; leer = offline)")]
public string FlexQueryId { get; set; } = "";
[Category("Accounting")]
[DisplayName("Flex-Token")]
[Description("IBKR Flex Web Service Token (Zielland; verschlüsselt ablegen, nicht im Klartext)")]
[PasswordPropertyText(true)]
public string FlexToken { get; set; } = "";
public override string ToString() => $"Basis {BaseCurrency}, alle {IngestIntervalHours} h";
}
// ─── Supervisor ──────────────────────────────────────────────────────────────
[TypeConverter(typeof(ExpandableObjectConverter))]
public class SupervisorSettings
{
[Category("Supervisor")]
[DisplayName("Modell")]
[Description("OpenRouter-Modell für die Analyse (Standard: openrouter/auto)")]
public string Model { get; set; } = "openrouter/auto";
[Category("Supervisor")]
[DisplayName("Token-Budget/Analyse")]
[Description("Weiches Token-Budget je Analyse-Lauf (Hinweis; harte Grenze = max. Tool-Iterationen)")]
public int TokenBudgetPerRun { get; set; } = 60000;
public override string ToString() => Model;
}
// ─── Deploymentcenter ────────────────────────────────────────────────────────
[TypeConverter(typeof(ExpandableObjectConverter))]
public class DeploymentcenterSettings
{
[Category("Deploymentcenter")]
[DisplayName("API Token")]
[Description("Authorization: Bearer . Ohne Token: keine Heartbeats, keine Fehlermeldungen.")]
[PasswordPropertyText(true)]
public string ApiToken { get; set; } = "";
[Category("Deploymentcenter")]
[DisplayName("Heartbeat aktiv")]
[Description("Dead-Man's-Switch an/aus")]
public bool HeartbeatEnabled { get; set; } = true;
[Category("Deploymentcenter")]
[DisplayName("Monitor Source")]
[Description("Monitor-Name im Dashboard. Leer = Rechnername (Auto-Registrierung beim ersten Ping)")]
public string MonitorSource { get; set; } = "";
[Category("Deploymentcenter")]
[DisplayName("Monitor Instance")]
[Description("Für mehrere Instanzen desselben Dienstes (z. B. EU/US)")]
public string MonitorInstance { get; set; } = "default";
[Category("Deploymentcenter")]
[DisplayName("Heartbeat-Intervall (Sekunden)")]
[Description("warning nach 2×, down nach 4× dieses Intervalls")]
public int HeartbeatIntervalSeconds { get; set; } = 60;
[Category("Deploymentcenter")]
[DisplayName("Fehler melden")]
[Description("Error/Fatal an den Fehler-Stream (/api/errors/v1/report)")]
public bool ReportErrors { get; set; } = true;
[Category("Deploymentcenter")]
[DisplayName("Update-Prüfung beim Start")]
[Description("Prüft nur, installiert nichts von selbst")]
public bool CheckUpdatesOnStart { get; set; } = true;
[Category("Deploymentcenter")]
[DisplayName("Update-Kanal")]
[Description("prod, beta oder dev")]
public string UpdateChannel { get; set; } = "prod";
[Category("Deploymentcenter")]
[DisplayName("Lizenzschlüssel")]
[Description("Nur Rückfall, falls noch kein Schlüssel im verschlüsselten Cache liegt")]
[PasswordPropertyText(true)]
public string LicenseKey { get; set; } = "";
public override string ToString() => HeartbeatEnabled ? "Aktiv" : "Deaktiviert";
}
// ─── 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();
[Category("Accounting")]
[DisplayName("Accounting")]
[Description("Buchhaltung/Reporting (unabhängiger Kontoauszug-Ingest)")]
public AccountingSettings Accounting { get; set; } = new();
[Category("Supervisor")]
[DisplayName("Supervisor")]
[Description("KI-Analyse/Forensik (OpenRouter, read-only)")]
public SupervisorSettings Supervisor { get; set; } = new();
[Category("Deploymentcenter")]
[DisplayName("Deploymentcenter")]
[Description("Lizenz, Watchdog, UpdateService, Fehler-Stream (https://dc.mhdf.de)")]
public DeploymentcenterSettings Deploymentcenter { get; set; } = new();
}