R9: Echter IbkrBrokerClient über die TWS API

Broker-Adapter gegen TWS/IB Gateway, aktivierbar über IBKRSettings.UseTwsApi;
NullBrokerClient bleibt Default. TradingEnabled bleibt als zweite, unabhängige
Sicherung bestehen – ohne ihn platziert der ExecutionService keine Order.

Aufteilung (src/IBKRTrader.Core/Trading/Ibkr/):
- IbkrMapping      – reine Abbildung Core <-> TWS (Kontrakt, Order, Kurs, Port-
                     und Statusregeln), vollständig unit-getestet
- IbkrConnection   – Socket-Lebenszyklus, Reader-Thread, reqId-Korrelation über
                     TaskCompletionSource
- IbkrBrokerClient – implementiert IBrokerClient, übersetzt Fehler in leere
                     Ergebnisse (Konto 0 lässt die Risikoprüfung alles ablehnen)

Bewusste Entscheidungen:
- Träges Verbinden mit Wiederholung statt Verbindungsaufbau beim Start: TWS ist
  nach einem Neustart minutenlang nicht bereit.
- Port wird gegen den Handelsmodus geprüft; Paper-Modus auf Live-Port (oder
  umgekehrt) lässt den Broker inaktiv, statt auf dem falschen Konto zu handeln.
- MarketDataType Default 4: Paper-Konten ohne Datenabo bekommen sonst keine Kurse.
- Fehlercode 10167 ist ein Statushinweis (verzögerte Daten folgen), kein Fehler.
  Als Fehler behandelt scheiterte jede einzelne Kursabfrage.

Verifiziert gegen Paper-Konto DUR371528: Verbindung, Konto (100.105,50 EUR),
Kurse (AAPL/MSFT/NVDA, verzögert), Fehlerpfade. Orderpfad bis zur Broker-Annahme
per What-If-Order geprüft (Aktie + Option, ohne Ausführung); dabei zugleich die
Optionsberechtigung des Kontos bestätigt. Offen: echte Ausführung (Fill ->
Buchung) und asynchrone Fill-Verfolgung – beides in IBKR-Integration.md notiert.

Doku: TWS-Setup-Checkliste.md (Einstellungen für Neuinstallation) neu,
IBKR-Integration.md / ARCHITECTURE.md / README.md nachgezogen.

154/154 Tests grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-04 17:40:00 +02:00
co-authored by Claude Opus 5
parent 2a312ca035
commit 80afcd49c1
14 changed files with 1427 additions and 45 deletions
+30 -1
View File
@@ -63,7 +63,36 @@ public class IBKRSettings
[Description("Eindeutige Client-ID für die API-Verbindung")]
public int ClientId { get; set; } = 1;
public override string ToString() => $"{Host}:{Port} (Client {ClientId})";
[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) ────────────────────────────────────