Files
IBKRTrader/docs/IBKR-Integration.md
T
RichardandClaude Opus 5 80afcd49c1 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>
2026-08-04 17:40:00 +02:00

128 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IBKR-Anbindung (TWS API via IB Gateway)
**Entscheidung:** Der `IbkrBrokerClient` (Alternative zum `NullBrokerClient`) nutzt die **TWS API**
über das **IB Gateway** native C#-Lib, robusteste Order-Ausführung, region-agnostisch.
> **Stand 2026-07-31:** Der Adapter ist implementiert (`src/IBKRTrader.Core/Trading/Ibkr/`) und wird
> über `IBKRSettings.UseTwsApi` aktiviert. Gegen das Paper-Konto **DUR371528** verifiziert sind
> Verbindung, `GetAccountStateAsync` und `GetQuoteAsync` (verzögerte Kurse, siehe unten) sowie der
> Fehlerpfad bei unbekanntem Symbol und nicht erreichbarer TWS.
> **Noch nicht verifiziert: `PlaceOrderAsync`** dafür ist eine echte Order auf dem Paper-Konto nötig.
## Regionen (US ↔ IE)
Gleiche API, gleiche Gateway-Software, **eine Codebasis**. Unterschied nur: Login/Entity
(Paper: IBKR Ireland, Live später: IBKR LLC US), Marktdaten-Abos, Reports **kein Code-Fork**.
Paper vs. Live = Konto + Port, kein Code-Unterschied.
## Betrieb (operativ)
- **Setup auf neuem System:** alle nötigen TWS-/Gateway-Einstellungen Schritt für Schritt in der
[TWS-Setup-Checkliste](TWS-Setup-Checkliste.md).
- **IB Gateway** (schlank, statt TWS) muss laufen und eingeloggt sein.
- **Ports:** Paper **4002**, Live **4001** (Auswahl über `TradingSettings.Mode`).
→ passt exakt zu den bestehenden `IBKRSettings` (Host `127.0.0.1`, Port, `ClientId`).
- **2FA / Dauerbetrieb:** IBKR erzwingt 2FA und täglichen Neustart. Für unbeaufsichtigten
Server-Betrieb **IBC/IBController** (Auto-Login + geplanter Neustart).
## Bibliothek
Offizielle C#-API als NuGet-Mirror **`IB.TWS.CSharpApi` 9.76.1**, referenziert im Core und in der
NuGet-Allowlist (`NuGet.config`) freigegeben. Das Paket zielt auf .NET Framework, ist aber reiner
managed Code und läuft auf net10 `NU1701` ist in der `.csproj` bewusst unterdrückt.
## Umsetzung (`src/IBKRTrader.Core/Trading/Ibkr/`)
Die TWS API ist **callback-basiert** (`EClientSocket` sendet, `EWrapper` empfängt Events). Das ist
hinter dem bestehenden async-Seam `IBrokerClient` gekapselt, aufgeteilt in drei Dateien:
| Datei | Aufgabe |
|---|---|
| `IbkrMapping.cs` | Reine Abbildung Core ↔ TWS (Kontrakt, Order, Kurs, Port-/Statusregeln) **unit-getestet** |
| `IbkrConnection.cs` | Socket-Lebenszyklus, Reader-Thread, reqId-Korrelation über `TaskCompletionSource` |
| `IbkrBrokerClient.cs` | Implementiert `IBrokerClient`, übersetzt Fehler in leere Ergebnisse |
**Ablauf je Methode**
- **`GetQuoteAsync`** → `reqContractDetails` (Ergebnis wird gecacht) → `reqMktData` als Snapshot →
`Quote`. Preis-Reihenfolge: letzter Handelspreis, sonst Bid/Ask-Mitte, sonst Schlusskurs.
- **`GetAccountStateAsync`** → `reqAccountSummary` (NetLiquidation, AvailableFunds).
- **`PlaceOrderAsync`** → Kontrakt auflösen → `placeOrder`; das Ergebnis kommt über `orderStatus`,
die Order-IDs liefert `nextValidId`.
**Bewusste Entscheidungen**
- **Träges Verbinden mit Wiederholung statt Verbindungsaufbau beim Start.** TWS ist nach einem
Neustart minutenlang nicht bereit (erst abgelehnte Verbindungen, dann abgebrochene Handshakes);
ein einmaliger Versuch beim Programmstart würde das nicht überleben.
- **Port-Prüfung gegen den Handelsmodus.** Paper-Modus auf einem Live-Port (oder umgekehrt) wird
abgelehnt, der Broker bleibt dann inaktiv sonst würde echtes Geld bewegt, wo ein Test erwartet wird.
- **Fehler werden nie geworfen, sondern zu leeren Ergebnissen.** Konto 0 lässt die Risikoprüfung
jedes Signal ablehnen die sichere Richtung bei nicht erreichbarem Broker.
- **`MarketDataType` standardmäßig 4 (verzögert + Frozen).** Paper-Konten ohne Datenabo bekommen
sonst überhaupt keine Kurse, und der `ExecutionService` überspringt jedes Signal mit „kein Kurs".
- **Zwei Sicherungen bleiben:** `UseTwsApi` entscheidet über den Adapter, `TradingEnabled` über den
Handel. Ohne den zweiten Schalter platziert der `ExecutionService` keine Order.
### Bekannte Grenze: Orders ohne sofortige Ausführung
`IBrokerClient.PlaceOrderAsync` ist synchron gedacht (Ausführung oder Fehlschlag). Eine Order, die
innerhalb von `OrderTimeoutSeconds` keinen Endstatus erreicht etwa eine Limit-Order im Orderbuch
oder eine Market-Order außerhalb der Handelszeiten , wird als Fehlschlag zurückgegeben, **kann bei
IBKR aber weiterhin aktiv sein**. Die Meldung enthält deshalb Order-ID und letzten Status. Sauber
lösen ließe sich das nur mit asynchroner Fill-Verfolgung (Order-Zustand persistieren, `orderStatus`
und `execDetails` dauerhaft mitschreiben) das ändert den Seam und ist eigene Arbeit.
## Marktdaten
Das Paper-Konto hat **kein Marktdaten-Abo**. TWS meldet deshalb bei jeder Kursanfrage den Hinweis
**10167** („nicht abonniert, es werden verzögerte Marktdaten angezeigt") und liefert danach ganz
normal verzögerte Ticks. Das ist ein **Statushinweis, kein Fehler** wird er als Fehler behandelt,
bricht die Kursanfrage ab, bevor die Ticks eintreffen, und jedes Symbol liefert „kein Kurs".
`IbkrMapping.IsInformational` deckt den Code deshalb ausdrücklich ab (mit Test).
Verifiziert am 2026-07-31 gegen DUR371528: AAPL 302,94 / MSFT 476,88 / NVDA 209,81 (verzögert).
## Optionen (Berechtigung verifiziert 2026-08-04)
Das Paper-Konto **DUR371528 ist für den Optionshandel freigeschaltet**. Geprüft ohne jede Ausführung
über eine **What-If-Order** (`Order.WhatIf = true`): IBKR rechnet dabei Margin und Gebühren durch die
komplette Prüfkette inklusive Handelsberechtigung und verwirft die Order anschließend.
| Prüfung | Ergebnis |
|---|---|
| `reqSecDefOptParams` AAPL | 24 Verfallstermine, 127 Strikes (5600), Multiplier 100, TradingClass AAPL |
| Börsen | SMART, CBOE, ISE, EMERALD, NASDAQBX, PHLX, AMEX, MEMX, PSE |
| Kontrakt `AAPL 20260812 C302.5` | aufgelöst, ConId 906106141, LocalSymbol `AAPL 260812C00302500` |
| What-If BUY 1 Kontrakt | **angenommen**, Init-Margin 0 → 589,52 |
| What-If BUY 1 AAPL (Aktie) | **angenommen**, Init-Margin 0 → 87,61, Kommission 1,00 USD |
Fehlte die Berechtigung, hätte IBKR die What-If-Order mit einem Berechtigungsfehler abgelehnt statt
eine Margin zu liefern. Für **Realtime**-Optionskurse wäre zusätzlich ein OPRA-Abo nötig; ohne Abo
kommen verzögerte Daten (siehe Marktdaten unten). Modul-Konzept:
[konzepte/KONZEPT-Modul-OptionsWheel.md](konzepte/KONZEPT-Modul-OptionsWheel.md).
> **What-If als Testwerkzeug:** Damit lässt sich der gesamte Orderpfad bis zur Broker-Annahme prüfen,
> ohne eine Position zu eröffnen. Der Adapter nutzt es nicht produktiv für Vorabprüfungen
> (Margin-Deckung vor einer echten Order) wäre es aber ein naheliegender Ausbau.
### Historie: welcher Gateway?
Aktuell laufen die Marktdaten-Worker über die **Client Portal Web API** (`IBKRGatewayService`).
Später zu entscheiden:
- Broker (Quotes/Konto/Orders) → TWS API; Marktdaten-Historie **vorerst auf CP Web API belassen**, oder
- Historie ebenfalls auf TWS (`reqHistoricalData`) migrieren → nur noch ein Gateway.
## Konfiguration (`settings.json`, Abschnitt `IBKR`)
| Schlüssel | Default | Bedeutung |
|---|---|---|
| `UseTwsApi` | `false` | Aktiviert den echten Adapter; sonst `NullBrokerClient` |
| `Host` / `Port` / `ClientId` | `127.0.0.1` / `4002` / `1` | Verbindung; Port muss zum Handelsmodus passen |
| `MarketDataType` | `4` | 1 Realtime, 2 Frozen, 3 verzögert, 4 verzögert+Frozen |
| `ConnectTimeoutSeconds` | `15` | Socket-Verbindung und Handshake |
| `RequestTimeoutSeconds` | `15` | Kurs-, Kontrakt- und Kontoantworten |
| `OrderTimeoutSeconds` | `60` | Wartezeit auf den Endstatus einer Order |
## Testbarkeit
- `IbkrMapping` ist vollständig unit-getestet (`tests/.../IbkrMappingTests.cs`): Port-/Modus-Regeln,
Kontrakt- und Order-Aufbau, Kursableitung, Tick- und Statusklassifizierung.
- Verbindung, Kurse und Orders bleiben **manuelle Verifikation gegen das Paper-Gateway** siehe
[TWS-Setup-Checkliste](TWS-Setup-Checkliste.md), Abschnitt Verifikation.
## Offen
1. `PlaceOrderAsync` gegen das Paper-Konto verifizieren (Order → Fill → Buchung).
2. IBC für Auto-Login/Neustart einrichten (Server-Betrieb).
3. Asynchrone Fill-Verfolgung, siehe „Bekannte Grenze" oben.
4. Entscheiden, ob die Marktdaten-Historie von der CP Web API auf `reqHistoricalData` wandert.