R10: Lesender Bestandsabgleich (IBrokerPortfolioReader) + Datenlage-Konzepte

Eigener Seam neben IBrokerClient: Wer handelt, braucht ihn nicht; wer die
eigene Buchfuehrung gegen den Broker abstimmt, braucht nur ihn. Zuteilung
und Verfall aendern Positionen ohne Order von uns - ohne Abgleich laeuft
das Managementbuch zwangslaeufig auseinander.

- IBrokerPortfolioReader mit GetPositionsAsync/GetExecutionsAsync;
  implementiert von IbkrBrokerClient und NullBrokerClient (DI registriert
  beide Rollen auf derselben Instanz).
- IbkrConnection: reqAccountUpdates statt reqPositions (nur dieser Weg
  liefert Marktwert und unrealisierten G/V), reqExecutions inkl. Zuordnung
  der verspaetet eintreffenden commissionReport-Callbacks ueber die ExecId.
- BrokerPosition/BrokerExecution als Broker-Wahrheit neben Position;
  IbkrMapping: ParseSide, ParseExecutionTime, FormatExecutionFilterTime
  (UTC wegen TWS-Warnung 2174) - mit Unit-Tests.
- Verifiziert gegen Paper-Konto DUR371528: 2 Positionen, 2 Ausfuehrungen
  inkl. Kommissionen.

Doku: Kapital- und Buchmodell (drei Wahrheiten, Kapitalzuteilung),
KONZEPT-Datenlage-und-Strategien (gemessen, was die API auf diesem Konto
liefert). Options-Wheel: Greeks bei verzoegerten Daten funktionieren
(Feld 83); Earnings-Termine sind ueber die TWS API nicht erreichbar
(Fehler 10358) - Behelf ueber IV-Filter statt Fremddatenquelle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-06 10:51:51 +02:00
co-authored by Claude Opus 5
parent 80afcd49c1
commit b96a20788c
13 changed files with 1039 additions and 20 deletions
+1
View File
@@ -128,6 +128,7 @@ Pin `new MariaDbServerVersion(new Version(11, 8, 6))`. Verbindung aus `appsettin
- [x] **Echter `IbkrBrokerClient` über die TWS API** (2026-07-31): `src/IBKRTrader.Core/Trading/Ibkr/` `IbkrMapping` (rein, unit-getestet), `IbkrConnection` (Socket + reqId-Korrelation), `IbkrBrokerClient` (`IBrokerClient`). Aktivierung über `IBKRSettings.UseTwsApi`; `NullBrokerClient` bleibt Default. Design und Grenzen: [IBKR-Integration.md](IBKR-Integration.md), Einstellungen: [TWS-Setup-Checkliste.md](TWS-Setup-Checkliste.md).
- [x] Gegen Paper-Konto DUR371528 verifiziert: Verbindung, Konto (NetLiquidation 100.105,50 EUR), Kurse (AAPL/MSFT/NVDA, verzögert) und Fehlerpfade
- [x] Orderpfad bis zur Broker-Annahme per **What-If-Order** verifiziert (Aktie + Option, keine Ausführung); **Optionsberechtigung im Paper-Konto bestätigt**
- [x] **`IBrokerPortfolioReader`** (Bestand + Ausführungen beim Broker) eigener Seam neben `IBrokerClient`, Grundlage für den Abgleich der eigenen Buchführung; gegen DUR371528 verifiziert (2 Positionen, 2 Ausführungen inkl. Kommissionen)
- [ ] **Offen:** `PlaceOrderAsync` mit echter Ausführung verifizieren (Fill → Buchung); asynchrone Fill-Verfolgung (Orders ohne sofortige Ausführung)
- [ ] IBKR-Account-Credentials mit `EncryptedStringConverter` speichern
+5
View File
@@ -97,6 +97,11 @@ kommen verzögerte Daten (siehe Marktdaten unten). Modul-Konzept:
> **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.
Welche Daten die API auf diesem Konto tatsächlich liefert und welche Strategien das trägt
steht gemessen in [konzepte/KONZEPT-Datenlage-und-Strategien.md](konzepte/KONZEPT-Datenlage-und-Strategien.md).
Kurz: Kurshistorie (30 Jahre), Volatilitätshistorie, Optionsketten und Griechen ja;
Fundamentaldaten und Marktscanner nein (Abo nötig).
### Historie: welcher Gateway?
Aktuell laufen die Marktdaten-Worker über die **Client Portal Web API** (`IBKRGatewayService`).
Später zu entscheiden:
+475
View File
@@ -0,0 +1,475 @@
# Kapital- und Buchmodell
> **Status: Konzept (2026-08-03) noch nicht implementiert.**
> Dieses Dokument beschreibt, wie mehrere Strategien gleichzeitig auf *einem* IBKR-Konto mit *einem*
> Guthaben arbeiten können, ohne sich gegenseitig zu stören, und wie das gegen die steuerliche
> Buchführung abgegrenzt ist. Es dient als Referenz, gegen die die spätere Umsetzung geprüft wird.
## 0. Problemstellung und Abgrenzung
TWS lässt sich pro Rechner nur einmal betreiben wir sind an **ein** Konto mit **einem** Guthaben
gebunden. Trotzdem sollen mehrere Strategie-Module parallel handeln. Daraus folgen vier Anforderungen:
1. Jedes Modul bekommt eine **klar begrenzte Kapitalmenge**, die es binden darf.
2. Jede Position hat einen **eindeutigen Eigentümer**. Kein Modul darf die Position eines anderen
Moduls oder eine manuell angelegte Position anfassen.
3. Das Konto darf durch Modul-Handel **niemals ins Minus oder in Margin** laufen.
4. Die **steuerliche Buchführung** ist davon vollständig unabhängig und muss zu 100 % stimmen.
Nicht Gegenstand dieses Dokuments: Strategielogik, Signalerzeugung, Marktdatenversorgung.
---
## 1. Die drei Wahrheiten
Es gibt drei Datenquellen mit unterschiedlicher Autorität. Sie dürfen sich nicht vermischen.
| Ebene | Quelle | Autoritativ für | Latenz |
|---|---|---|---|
| **Steuerbuch** (`acc_`) | Flex Query | Geld, Steuer, GuV alles gegenüber der Steuerbehörde | T+1 |
| **Depot-Ist** | TWS-API-Snapshot | Was *jetzt* real im Konto liegt | Sekunden |
| **Managementbuch** (`core_`) | eigene Order-Events + Abgleich | Zuordnung von Positionen zu Büchern | live |
**Harte Regel:** Das Managementbuch beeinflusst das Steuerbuch **niemals** weder korrigierend noch
ergänzend. Der Accounting-Ingest liest Flex Query, klassifiziert und bucht; ob dabei eine `BookId`
bekannt ist, ist ihm gleichgültig. Die Modulzuordnung ist eine *optionale, nicht-autoritative*
Beistelltabelle (Flex-Trade-ID ↔ BookId). Fehlt sie oder ist sie falsch, ändert sich am Steuerergebnis
exakt nichts.
Umgekehrt gilt: Das Managementbuch **bezieht** Korrekturen aus dem Steuerbuch (Gebühren, Dividenden,
Splits), nie andersherum.
---
## 2. Bücher (Books)
Ein **Buch** ist ein Kapitaltopf mit zugeordneten Positionen. Bücher sind:
| Buch | Bedeutung | Handelt | Limits gelten |
|---|---|---|---|
| je Strategie (`CT`, …) | ein Modul, ggf. ein Parametersatz | ja | ja |
| `MANUAL` | von Hand in TWS/IBKR angelegt | nein (nur extern) | **nein**, zählt aber mit |
| `UNASSIGNED` | am Broker gefunden, keinem Buch zugeordnet | nein | zählt mit |
| `HOUSE` | nicht allokierte Reserve, FX, Ausgleichsposten | nein | |
**`BookId` ist ein eigenes Konzept, nicht der Modulname.** Default `BookId == Modulname`, aber die
Trennung erlaubt später mehrere Bücher desselben Moduls und macht `MANUAL`/`UNASSIGNED`/`HOUSE`
sauber modellierbar. Jetzt kostenlos, später teuer nachzurüsten.
### Eigentumsregeln (im Core erzwungen, nicht per Konvention)
1. Ein Verkaufssignal von Buch *b* kann **nur Positionen von *b*** reduzieren, gedeckelt auf
`Menge(b, Symbol)`. Darüber hinaus wird gekappt **niemals** wird ein Short erzeugt.
2. `MANUAL` und `UNASSIGNED` senden keine Signale und empfangen keine.
3. Ein Modul kann seine `BookId` **nicht selbst wählen**. Sie stammt aus der Modul-Registrierung und
wird serverseitig gesetzt; `TradeSignal.SourceModule` aus dem Modul ist nicht vertrauenswürdig.
4. Module bekommen `IBrokerClient` **nicht** per DI ausschließlich `IExecutionService`.
Sonst ist die gesamte Buchführung umgehbar.
5. Ein Kill-Switch/Supervisor darf alles liquidieren als explizite Core-Autorität mit eigener
Konfiguration, standardmäßig unter Ausschluss von `MANUAL`.
---
## 3. Kapitalmodell: ein Pool, Obergrenzen
**Es gibt keinen Kapitaltransfer zwischen Büchern.** Kein Buch-Cash, keine Rebalancing-Läufe, keine
Ausgleichsforderungen. Stattdessen ein gemeinsamer Cash-Pool und pro Buch eine Obergrenze:
```
Verteilbar = FreiesCash(gehaircuttet) + Σ Modul-Positionswert HouseReserve
Cap(b) = konfigurierter Anteil × Verteilbar (Default: Gleichverteilung)
Gebunden(b) = Marktwert der Positionen von b + offene Reservierungen von b
Headroom(b) = min( Cap(b) Gebunden(b), freies Pool-Cash )
```
### Eigenschaften, die bewusst so sind
- **Gewinne verteilen sich von selbst.** Realisiert ein Buch einen Gewinn, wächst der gemeinsame Pool;
da alle Caps aus `Verteilbar` abgeleitet werden, steigt der Headroom **aller** Bücher. Diversifikation
ohne einen einzigen Zwangsverkauf.
- **Ein Buch darf über seinem Cap liegen.** Steigen die Positionen im Wert, passiert nichts außer
dass nicht mehr nachgekauft werden darf. Der Zustand „über Cap" ist **legal**, wird angezeigt und
gemeldet, aber **nie geheilt**. Es wird niemals eine Position geschlossen, nur um eine Zielquote
herzustellen.
- **`Σ Caps ≤ 100 % Reserve` wird beim Start validiert.** Sonst könnte ein schnelles Buch ein
langsames aushungern („wer zuerst kommt").
- **Die Modul-Welt sieht nie den NAV**, sondern nur ihr eigenes Kapital plus freies Cash. Manuelle
Positionen auch kreditfinanzierte sind eine Black Box, die nur über das freie Cash wirkt.
### Kapitalführung ≠ Performance-Messung
Die Kapitalführung ist ein Pool plus Caps. Die GuV **pro Buch** ist eine reine *Auswertung* über die
Trade-Historie (für Supervisor und Reporting) und steuert nichts. Diese Entkopplung ist beabsichtigt.
---
## 4. Keine Margin die Sperre
**Modul-Handel ist strikt cash-only. Manueller Handel darf Margin nutzen.** Der Kontotyp ist
Cash-Konto; brokerseitig kann trotzdem Margin verfügbar sein, deshalb ist die Software-Sperre
zwingend und testpflichtig.
### Schicht A richtige Bemessungsgrundlage
`BuyingPower` und `AvailableFunds` sind auf einem Margin-Konto bereits gehebelt gerechnet und als
Basis unbrauchbar. Maßgeblich ist:
```
FreiesCash = min(TotalCashValue, AvailableFunds) HouseReserve Σ offene Reservierungen
```
`TotalCashValue` ist das echte Bargeld. Eröffnest du manuell eine Margin-Position, sinkt es, und die
Modul-Kaufkraft schrumpft automatisch mit. Bei `TotalCashValue ≤ 0` ist die Modul-Kaufkraft null
ohne Sonderlogik. Module können einen manuellen Kredit strukturell nicht ausweiten.
### Schicht B kein Short
Verkäufe werden auf die Buchmenge gekappt. Shorts sind implizit Margin.
### Schicht C Währung
Ein Kauf in Währung X darf **nur aus Cash in Währung X** finanziert werden. Andernfalls entsteht ein
Sollsaldo in X ein Margin-Kredit, auch wenn das Konto in Summe positiv aussieht. Buch-Cash und
Reservierungen werden deshalb **pro Währung** geführt (siehe Abschnitt 7).
### Schicht D Watchdog
Ein Hosted Service prüft Cash und Margin-Kennzahlen laufend. Bei Unterschreitung: **globaler
Kauf-Stopp + Alarm** und **niemals automatisches Verkaufen** zur Heilung. Ein
Selbstheilungs-Amoklauf ist gefährlicher als der Zustand, den er beheben soll.
---
## 5. Reservierungen (Order-Lebenszyklus)
Verhindert, dass zwei Module gleichzeitig dasselbe Geld ausgeben. Persistent in der DB, nicht im RAM.
```
INTENT → RESERVED → SUBMITTED → FILLED | PARTIAL | CANCELLED | REJECTED | EXPIRED
```
- **Reservierungshöhe:** `Menge × Referenzpreis × (1 + Puffer)` + geschätzte Kommission.
Puffer klein bei Limit-Orders (das Limit *ist* die Obergrenze), größer bei Market (Slippage),
zusätzlich FX-Puffer bei währungsübergreifenden Vorgängen.
- **Atomar** gegen Buch-Headroom *und* Pool-Cash, in einer DB-Transaktion hinter einem **globalen
Order-Lock**. Bei einer TWS-Verbindung reicht ein einfacher `SemaphoreSlim`; feingranulare Locks
wären hier nur eine zusätzliche Fehlerquelle.
- **Freigabe** beim Terminalzustand; Differenz zwischen Reservierung und echtem Fill fließt zurück.
- **TTL zwingend.** Erreicht eine Reservierung ohne Terminalzustand ihr Ablaufdatum oder den
Handelsschluss → **Alarm, keine stille Freigabe**. Eine hängende Order, deren Reservierung
freigegeben wird, gibt das Geld zweimal aus.
- **Idempotenz** über `SignalId` bzw. eine Client-Order-ID, damit ein Reconnect keine Doppelorder
erzeugt.
- **Crash-Recovery:** beim Start alle nicht-terminalen Reservierungen gegen die offenen Broker-Orders
abgleichen; Waisen → Quarantäne + Alarm.
---
## 6. Limits und Feedback an die Module
### Konzentrationslimit pro Einzelwert
```
SymbolExposure(s) = Σ_b Marktwert(b, s) ← über ALLE Bücher, inkl. MANUAL
Prüfung: SymbolExposure(s) + geplanter Kauf ≤ MaxSymbolPercent × NAV
```
- **NAV als Nenner**, nicht der Positionswert sonst wird das Limit bei viel Cash absurd eng und bei
wenig Cash absurd weit.
- **`MANUAL` ist von der Prüfung befreit, zählt aber voll in den Zähler.** Hältst du 50 % AAPL von
Hand, sind die Module bei AAPL vollständig gesperrt. So gewollt.
- **Verkäufe sind nie limitiert.** Ein Limit darf niemals das Schließen einer Position verhindern.
### Feedback-Kontrakt
Das Gate ist **hart**. Das Modul entscheidet nur, *wie es auf die Ablehnung reagiert* nicht, ob das
Limit gilt. Ein erneut gesendetes zu großes Signal wird wieder abgelehnt. Der Core trimmt nicht
selbstständig; die Entscheidung liegt auf Strategieebene.
```csharp
record LimitBreach(
LimitKind Kind, // SymbolConcentration | BookCap | CashAvailable
// | FxBuffer | CurrencyNotAllowed | VenueNotAllowed
string Scope, // "AAPL" | "CT" | "USD"
decimal Current,
decimal Limit,
int MaxQuantity, // was jetzt noch ginge
decimal MaxNotional);
```
Ergänzend eine Vorab-Query `GetHeadroom(book, symbol)`, damit ein gut gebautes Modul gar nicht erst
gegen die Wand fährt und seine Order gleich richtig dimensioniert.
**Drossel:** Ein Modul, das dasselbe Signal wiederholt gegen dieselbe Wand schickt, wird nach N
Versuchen pro `SignalId` gedämpft und protokolliert. Sonst produziert eine schlecht geschriebene
Strategie Logfluten und TWS-Last.
---
## 7. Währungen und Handelsplätze
**Kontobasiswährung:** konfigurierbar, bei Ersteinrichtung festgelegt, danach gesperrt
(siehe Abschnitt 11 eine Änderung nach dem ersten Trade macht alle historischen Bewertungen,
Caps und GuV-Zahlen ungültig).
- Produktion (US LLC): `USD`
- Test/Paper (EU): `EUR`
### Contract-Auflösung ist Pflicht
Eine Order mit nacktem Symbol-String über `SMART` kann bei mehrdeutigen Tickern still an einer Börse
in einer anderen Währung landen. Deshalb:
- Vor jeder Order wird der Contract über `reqContractDetails` aufgelöst; `currency`, `exchange`,
`primaryExchange` und `conId` werden geprüft.
- **Whitelist auf zwei Achsen:** Währung ∈ `AllowedCurrencies` **und** Handelsplatz ∈ konfigurierter
Liste. Verstoß → harte Ablehnung, unabhängig vom Modul.
- Das Ergebnis wird gecacht; **die `conId` wird in der Position gespeichert.** Sie ist IBKRs
eindeutige Instrumenten-ID und deutlich sicherer als ein Ticker.
### FX-Marge als Sicherheitspuffer, nicht als Exposure-Deckel
Bei überwiegendem Handel in Kontobasiswährung wäre ein FX-Exposure-Limit meist verletzt und damit
nutzlos. Sinnvoll wirkt die Marge an drei anderen Stellen:
1. **Bewertungs-Haircut:** Fremdwährungs-Cash geht mit Abschlag in `Verteilbar` ein → das eigene
Kapital wird nie überschätzt.
2. **Reservierungspuffer** bei währungsübergreifenden Käufen, zusätzlich zum Slippage-Puffer.
3. **FX-Exposure als Kennzahl mit Alarmschwelle** sichtbar und meldepflichtig, ohne Handelssperre.
### Kein Auto-FX im Order-Pfad
Fehlt Cash in der Zielwährung, scheitert der Modul-Kauf mit `LimitKind.CashAvailable`. Konvertiert
würde der Order-Pfad selbst, könnte ein Modul indirekt FX-Kosten und FX-Timing auslösen, und die
Reservierungslogik müsste zwei Währungen gleichzeitig sperren. FX bleibt ein expliziter Vorgang
auf `HOUSE`-Ebene.
---
## 8. Abgleich (Reconciliation)
Weil jederzeit manuell in TWS eingegriffen werden kann, ist der Abgleich kein Zusatz, sondern das
Fundament. Zwei Stufen:
**Stufe 1 Ledger ↔ TWS (minütlich):** Handelssicherheit. Stimmen Stückzahlen und Cash?
| Befund | Bedeutung | Reaktion |
|---|---|---|
| Broker > Ledger | manuell gekauft, oder ein Fill kam nach | Überschuss → `UNASSIGNED` |
| Broker < Ledger | manuell verkauft / Corporate Action | **Break:** betroffene Bücher und Symbol für neue Orders sperren, Alarm |
| Cash weicht ab | Gebühren, Zinsen, FX | Differenz gegen `HOUSE`, ab Schwelle Break |
Bei „Broker < Ledger" wird bewusst **nicht** automatisch korrigiert. Anteiliges Wegkürzen zerstört
die Zurechenbarkeit Halt-and-alert, der Mensch entscheidet.
**Stufe 2 Ledger ↔ Flex Query (täglich, nach Ingest):** Qualitätssicherung. Hier kommen Gebühren,
Dividenden, Quellensteuer, FX-Differenzen und Splits an Dinge, die die TWS-API gar nicht oder
schlecht liefert.
**Gebühren gehören dem verursachenden Buch.** Sonst subventioniert `HOUSE` die vieltradenden Module
und die Performance-Zahlen lügen. Beim Fill wird geschätzt, beim Flex-Abgleich exakt nachjustiert.
---
## 9. Unzugeordnete Positionen und Eskalation
Zustände: `New → Notified → Acknowledged → Assigned(Book)`, mit `DetectedAt`-Zeitstempel.
- Ein Watchdog meldet alles, was **länger als 30 Minuten** unbearbeitet liegt.
- **Backoff:** nach 30 min, dann 2 h, dann täglich sonst wird der Kanal unbrauchbar.
- **Abgestufte Sperrwirkung:** Eine `UNASSIGNED`-Position **zählt voll** in Exposure und
Konzentrationslimit, bremst die Module also automatisch, **sperrt aber nicht hart**. Hart gesperrt
wird nur der gefährliche Fall aus Abschnitt 8 (Broker < Ledger).
---
## 10. Benachrichtigungen
Im Code existiert derzeit **kein** Benachrichtigungssystem (die Treffer zu „Notification" im
Supervisor-MCP sind JSON-RPC-Notifications, etwas anderes). Sauberer Start.
```
INotificationSink Name, SendAsync(Notification, ct)
Notification Severity, Category, Title, Body, DedupKey, Data
NotificationService Fan-out über alle registrierten Sinks
```
Drei Eigenschaften müssen von Anfang an drin sein, weil sie später schwer nachzurüsten sind:
- **Outbox in der DB** (`core_notification`): erst persistieren, dann zustellen, Retry bei Fehler.
Solange kein Zielkanal existiert, sammeln sich Meldungen sichtbar an und werden zugestellt, sobald
ein Sink da ist. Nichts geht verloren.
- **Dedup/Throttling** über `DedupKey` (z. B. `unassigned:AAPL`).
- **Severity-Routing:** Info → Log/UI, Warning → Chat, Critical → alle Kanäle.
Ausbaureihenfolge: `LogSink` + `UiSink` sofort → `MatrixSink`, sobald der Server steht →
`TelegramSink` optional als Backup.
**Matrix** ist reines HTTP, keine Library nötig:
`PUT /_matrix/client/v3/rooms/{roomId}/send/m.room.message/{txnId}` mit dem Access-Token eines
Bot-Users. Die `txnId` ist Matrix' eingebauter Idempotenz-Schlüssel und passt exakt auf die
Outbox-ID. Das Token gehört in `SecretProtection`.
---
## 11. Steuerbuch: Jurisdiktionsprofil
**Pro Deployment gibt es genau eine Jurisdiktion, und sie wechselt nie.** Ein Kontowechsel zwischen
Ländern findet nicht statt die Option „später auch in Deutschland" bedeutet ausdrücklich: anderer
Server, andere Datenbank, anderes IBKR-Konto. Daraus folgt: keine Migration, keine
Mehrmandantenfähigkeit, kein Umschalten zur Laufzeit.
Das Profil ist **kein Währungs-Flag**, sondern ein Austausch der gesamten Lot- und
Klassifikationslogik:
| Aspekt | US LLC (Hauptziel) | DE privat (Option) | DE GmbH (Option) |
|---|---|---|---|
| Berichtswährung | USD, keine Umrechnung | EUR, Umrechnung pro Vorgang | EUR, Umrechnung pro Vorgang |
| Lot-Matching | FIFO Standard, Specific ID wählbar | FIFO zwingend | Bilanzierung |
| Wash Sale (30 Tage) | **ja**, Basis-Anpassung | kein Äquivalent | kein Äquivalent |
| Haltefrist | short/long term ab 1 Jahr | irrelevant | irrelevant |
| Verlustverrechnung | Carryforward mit Deckel | getrennte Töpfe | wieder anders |
| Quellensteuer | | Anrechnung nach DBA | Anrechnung |
| Ausgabe | 1099-B-Abgleich / K-1 | Anlage KAP | Bilanz/GuV |
Die **Wash-Sale-Regel** ist die einschneidendste: sie verändert Anschaffungskosten rückwirkend. Eine
Engine, die nur FIFO kann, lässt sich dafür nicht nachrüsten. Deshalb muss die Lot-Verwaltung von
Anfang an lot-basiert sein und **Basis-Anpassungen als eigene Buchungsart** kennen auch wenn das
erste Profil sie noch nicht nutzt.
> Die konkreten steuerlichen Ausprägungen (u. a. ob die LLC als Disregarded Entity oder Partnership
> behandelt wird, was das Reporting ändert) sind mit dem Steuerberater abzustimmen. Hier steht nur,
> was die Software mechanisch abbilden können muss.
### Profile und Umgebungen
```
Test/Paper (EU) BaseCurrency EUR TaxProfile None
Produktion (US) BaseCurrency USD TaxProfile UsLlc
Option DE BaseCurrency EUR TaxProfile DePrivat | DeGmbh
```
Das Profil **`None`/`Paper`** führt vollständig Buch, rechnet aber keine Steuer und ist in UI und
allen Exporten sichtbar als solches markiert. Deutsche Steuerregeln auf Paper-Trades zu rechnen wäre
sinnlos und produziert Zahlen, die jemand später für echt halten könnte.
### Wo der Lock hingehört
**Nicht in `appsettings.json`.** Eine Datei lässt sich editieren, und dann rechnet die Engine ab
morgen nach anderen Regeln über denselben Bestand der schlimmstmögliche Fehler, weil er sich still
auswirkt.
Der Lock gehört **in die Datenbank, neben die Daten, die er regiert**: eine Stempelzeile im
Accounting-Schema mit Jurisdiktion, Berichtswährung, Kontobasiswährung und Einrichtungszeitpunkt.
Beim Start wird die Konfiguration dagegen geprüft; bei Abweichung startet das Accounting-Modul nicht,
sondern meldet einen Konflikt. Gesetzt wird der Stempel bei der Ersteinrichtung, geändert nur durch
eine leere Datenbank.
### Was deshalb *nicht* gebaut wird
Die EZB-Kursanbindung (Kursquelle, Stichtagsregeln, Kursarchivierung) ist für das Hauptziel nicht
nötig: USD-Konto, USD-Reporting, keine Umrechnung. Also nur die **Naht** bauen (`IFxRateSource`, im
US-Profil eine No-Op), nicht die Implementierung. Wird die DE-Option je gezogen, geschieht das
ohnehin auf einem anderen Server dann wird sie dort ergänzt.
Falls doch: EZB-Referenzkurse autoritativ, IBKR-Bewertungskurse nur als Kontrollgröße; **verwendeter
Kurs, Quelle, Stichtag und Abrufzeitpunkt werden mitgespeichert**, nicht nur der umgerechnete Betrag.
---
## 12. Datenmodell-Deltas
| Heute | Delta |
|---|---|
| `core_position` (PK `Module`+`Symbol`) | `Module``BookId`; **`ConId`, `Currency`, `Exchange` ergänzen** |
| `core_budget` (Total/Used/MaxPerTrade) | ersetzen durch `core_book`: Cap-Regel, Flags (ReadOnly, Active), Mindestordergröße |
| | `core_reservation` (Zustandsmaschine, TTL, Währung) |
| | `core_broker_snapshot` + `core_reconciliation_break` |
| | `core_notification` (Outbox) |
| | `core_instrument` (aufgelöste Contracts, `conId`-Cache, Whitelist-Status) |
| | `acc_jurisdiction` (Stempel, Abschnitt 11) |
| ~~Buch-Cash, Transfers, Rebalance-Lauf~~ | **entfällt** ein Pool plus Caps (Abschnitt 3) |
| `RiskContext.NetLiquidation` | → `Verteilbar` und `Headroom(b)`, **nicht** NAV |
| `IbkrConnection`: `NetLiquidation,AvailableFunds` | + `TotalCashValue`, `SettledCash`, Margin-Kennzahlen, **pro Währung** |
| `IBrokerClient` | + `GetPositionsAsync`, + `ResolveContractAsync` |
| `ExecutionService` (Check-then-Act ohne Lock) | Order-Gateway davor: Serialisierung + Reservierung |
| `ExecutionResult.Reason` (string) | + strukturiertes `LimitBreach` (Abschnitt 6) |
---
## 13. Invarianten und Testkatalog
Die No-Margin-Bedingung wird als **eine ausführbare Invariante** formuliert, die an drei Stellen
dieselbe Codebahn nimmt:
```
∀ Währung c: FreiesCash(c) ≥ 0
∧ Σ offene Reservierungen(c) ≤ FreiesCash(c)
∀ Buch b, Symbol s: Menge(b, s) ≥ 0
∀ Symbol s: Σ_b Menge(b, s) = BrokerMenge(s)
```
1. als Assert am Ende **jedes** Trading-Tests (gemeinsamer Testhelfer, nicht pro Test neu geschrieben)
2. als Property-Test über zufällige Sequenzen aus Reservierung / Fill / Teilfill / Cancel / Slippage /
manuellem Eingriff
3. als Laufzeitprüfung im Watchdog **identische Implementierung**, damit Test und Produktion nicht
auseinanderlaufen
### Pflichtfälle
| Fall | Erwartung |
|---|---|
| Zwei parallele Reservierungen, zusammen > freies Cash | zweite abgelehnt |
| N nebenläufige Tasks (Stresstest) | Σ Reservierungen nie > freies Cash |
| Fill teurer als reserviert, innerhalb Puffer | ok, Restfreigabe korrekt |
| Fill jenseits des Puffers | Alarm, Cash bleibt ≥ 0 |
| Manuelle Margin-Position taucht auf, Cash < Reserve | alle Modul-Käufe gesperrt |
| Cash in Zielwährung = 0, andere Währung vorhanden | abgelehnt **kein** stiller Fremdwährungskredit |
| Verkaufssignal über Buchmenge hinaus | gekappt, kein Short |
| Reservierung erreicht TTL | Alarm, **keine** stille Freigabe |
| Neustart mit offenen Reservierungen | aus DB rekonstruiert, keine Doppelausgabe |
| Modul setzt fremde `BookId` im Signal | serverseitig überschrieben |
| Verkaufssignal gegen `MANUAL` | abgelehnt |
| Contract löst auf fremde Währung/Börse auf | abgelehnt vor Ordersendung |
---
## 14. Bewusst verschoben
- **Self-Cross-Netting** (Modul A kauft, Modul B verkauft dasselbe Symbol gleichzeitig). Bei der
erwarteten Handelsfrequenz unrealistisch und allenfalls eine Ausnahmeerscheinung.
**Billige Vorstufe jetzt:** Ist im Order-Gateway eine gegenläufige Order für dasselbe Symbol
pending, wird das protokolliert und gewarnt. Der Lock ist ohnehin da das kostet fast nichts und
liefert Daten darüber, ob das Problem je real wird.
- **Corporate Actions** (Splits, Spin-offs) in der Buchzuordnung vorerst über den Flex-Abgleich als
Break sichtbar, manuelle Zuordnung.
- **Echte Sub-Accounts** bei IBKR (Advisor/Family-Struktur) als brokerseitige Trennung. Erwogen und
verworfen: erfordert Kontotypwechsel und feste Vorabaufteilung des Kapitals, deutlich unflexibler
als virtuelle Bücher.
---
## 15. Offene Punkte
- **Mindestordergröße** pro Buch: ein Wert in Kontobasiswährung, Fremdwährungsorders per Tageskurs
dagegen geprüft. Konkreter Wert beim Bauen festzulegen.
- **Konkrete Limitwerte** (`MaxSymbolPercent`, `HouseReserve`, FX-Haircut, Slippage-Puffer, TTL)
Startwerte beim Bauen festzulegen und in `settings.example.json` dokumentieren.
- **Handelsplatz-Whitelist**: konkrete Börsenliste je Währung.
- **Umsetzungsreihenfolge** noch nicht besprochen.
---
## Zusammenfassung der getroffenen Entscheidungen
| # | Entscheidung |
|---|---|
| 1 | Cash-Konto; Modul-Handel strikt cash-only, manueller Handel darf Margin nutzen |
| 2 | Kein Kapitaltransfer zwischen Büchern ein Pool plus Obergrenzen; **niemals** aktives Schließen zum Balancieren |
| 3 | Limit-Verletzung: hartes Gate, strukturiertes Feedback; die Reaktion entscheidet die Strategie |
| 4 | Kontobasiswährung USD (Produktion) / EUR (Test), bei Ersteinrichtung gesetzt und danach gesperrt |
| 5 | Nur USD und EUR handelbar; Contract-Auflösung mit Währungs- und Handelsplatz-Whitelist ist Pflicht |
| 6 | Steuerbuch strikt getrennt, Flex Query als einzige Quelle, Jurisdiktionsprofil einmalig und in der DB verankert |
| 7 | Unzugeordnete Positionen: Quarantäne + Eskalation nach 30 Minuten mit Backoff |
| 8 | Benachrichtigungen über Outbox + Sink-Abstraktion; Matrix als Zielkanal, Telegram optional |
@@ -0,0 +1,206 @@
# Analyse: Datenlage über die TWS API und welche Strategien sie trägt
> Stand: 2026-08-04. **Alle Angaben in Abschnitt 1 und 2 sind gegen das laufende Paper-Gateway
> gemessen** (Konto DUR371528, TWS API 9.76.1, `MarketDataType = 4`), nicht aus der IBKR-Doku
> übernommen. Wo etwas nur plausibel, aber ungeprüft ist, steht es ausdrücklich dabei.
>
> Zweck: entscheiden, welche Strategien wir **ohne zusätzliche Datenanbieter** bauen können
> und welche wir uns sparen, weil die Datengrundlage fehlt.
---
## 0. Kurzfassung
Die TWS API deckt **Preis-, Volatilitäts- und Optionsdaten sehr gut ab** und **Fundamentaldaten
gar nicht**. Zwei Grenzen bestimmen den Zuschnitt jeder Strategie:
1. **Kein Screening.** Der Marktscanner ist gesperrt (Realtime-Abo nötig). Wir können den Markt
nicht nach Kandidaten durchsuchen jede Strategie muss auf einer **fest gepflegten Watchlist**
arbeiten. Das deckt sich mit der Festlegung im [OptionsWheel-Konzept](KONZEPT-Modul-OptionsWheel.md).
2. **Nur verzögerte Kurse (~15 Min).** Alles, was auf Intraday-Reaktion beruht, fällt weg.
Entscheidungen auf Tages- oder Wochenbasis sind davon **nicht** betroffen.
Innerhalb dieser Grenzen ist die Lage gut: 30 Jahre Kurshistorie, dividendenbereinigte Serien,
Volatilitätshistorie, vollständige Optionsketten und **funktionierende Griechen auch mit
verzögerten Daten**. Das trägt Prämienstrategien und Trendfolge auf Tagesbasis ohne jeden
externen Anbieter.
**Empfehlung:** OptionsWheel als erstes Modul weiterbauen es ist die Strategie mit dem besten
Verhältnis von vorhandener Datengrundlage zu Ertragserwartung, und die Voraussetzungen sind
inzwischen alle geprüft.
---
## 1. Was die API liefert (gemessen)
### 1.1 Kurshistorie die stärkste Säule
| Auflösung | Verfügbarer Zeitraum | Gemessen an AAPL |
|---|---|---|
| Monatsbars | **30 Jahre** | 361 Bars ab 1996-08 |
| Wochenbars | 15 Jahre | 783 Bars ab 2011-08 |
| Tagesbars | 10 Jahre | 2.511 Bars ab 2016-08 |
| Stundenbars | 2 Jahre | 3.488 Bars |
| Minutenbars | 30 Tage | 11.453 Bars |
| Einzelticks | `reqHistoricalTicks` | 351 Ticks für 2 Tage zurück |
Zusätzlich:
- **`ADJUSTED_LAST`** dividendenbereinigte Tageskurse. Wichtig: unbereinigte Serien erzeugen bei
jeder Ausschüttung ein Scheinsignal in Momentum- und Mean-Reversion-Rechnungen.
- **`HISTORICAL_VOLATILITY`** realisierte Volatilität als Zeitreihe (122 Tage je Abruf).
- **`OPTION_IMPLIED_VOLATILITY`** **IV-Historie des Basiswerts** (122 Tage je Abruf).
Längere Reihen lassen sich durch wiederholte Abrufe mit gesetztem `endDateTime` zusammensetzen.
Die letzten beiden sind der eigentliche Schatz: aus ihnen lässt sich **IV-Rank / IV-Perzentil**
rechnen die zentrale Kennzahl für jede Prämienstrategie (verkaufe Volatilität, wenn sie relativ
zu ihrer eigenen Geschichte teuer ist).
### 1.2 Optionen
| Datenart | Ergebnis |
|---|---|
| Optionskette (`reqSecDefOptParams`) | 24 Verfallstermine, 127 Strikes, Multiplier 100, TradingClass |
| Griechen (`tickOptionComputation`) | **IV, Delta, Gamma, Vega, Theta + Basiswertkurs** |
| Handelsberechtigung | vorhanden (What-If-Order angenommen) |
**Wichtigster Einzelbefund:** Die Griechen kommen **auch ohne Realtime-Abo**. Gemessen an
`AAPL 20260821 C305`: IV 0,2730 / Delta 0,5443 / Gamma 0,0220 / Vega 0,2626 / Theta 0,2304.
Sie laufen über die verzögerten Tick-Felder 8083, wobei **Feld 83 (Modell) die relevante Größe**
für eine delta-basierte Strike-Wahl ist.
Damit ist der offene Punkt 2 aus dem [OptionsWheel-Konzept](KONZEPT-Modul-OptionsWheel.md)
beantwortet: Die geplante delta-basierte Strike-Wahl im Zielband 0,150,30 ist umsetzbar, die
Ersatzlösung über prozentualen Abstand wird nicht gebraucht. Verzögerung heißt: Der Delta-Wert ist
~15 Minuten alt für die Auswahl eines Strikes mit 3045 Tagen Restlaufzeit ist das belanglos.
### 1.3 Stammdaten, Nachrichten, Sonstiges
| Datenart | Ergebnis |
|---|---|
| Kontraktstammdaten | Branche/Kategorie/Unterkategorie (AAPL: Technology / Computers / Computers), Langname, **Handelszeiten**, Zeitzone, MinTick, gültige Börsen |
| Symbolsuche (`reqMatchingSymbols`) | Fuzzy-Suche, liefert auch Indizes |
| Nachrichten | 3 Anbieter: **BRFG** (Briefing.com Markt), **BRFUPDN** (Analystenaktionen), **DJNL** (Dow Jones Newsletters) |
| Historische Nachrichten | funktioniert, Schlagzeilen mit Zeitstempel und Anbieter |
| Konto, Bestand, Ausführungen | siehe [IBKR-Integration.md](../IBKR-Integration.md) |
Die Branchenklassifikation ist brauchbar für **Klumpenrisiko-Prüfungen** ("nicht drei Positionen
im selben Sektor"). Die Handelszeiten sind operativ wichtig: Market-Orders außerhalb der RTH
bleiben ohne Fill hängen.
`BRFUPDN` ist bemerkenswert die Schlagzeilen sind maschinell auswertbar strukturiert:
```
2026-04-17 12:00:57 [BRFUPDN] !BNP Paribas Exane upgraded Apple (AAPL) to Outperform
2026-04-14 14:58:42 [BRFUPDN] !BofA Securities reiterated Apple (AAPL) coverage with Buy and target $325
```
---
## 2. Was die API nicht liefert
| Datenart | Fehler | Bedeutung |
|---|---|---|
| **Fundamentaldaten**, alle Reports (`ReportSnapshot`, `ReportRatios`, `ReportsFinStatements`, `RESC`, `CalendarReport`) | 10358 „Fundamentaldaten nicht zulässig" | Kein Refinitiv-Abo auf dem Konto |
| **Fundamentalkennzahlen-Tick 258** (KGV, Marktkapitalisierung, Dividendenrendite, Beta) | 10358 | dito |
| **Marktscanner-Ausführung** (alle getesteten scanCodes) | 492 „zusätzliche Berechtigungen" | Realtime-Marktdatenabo nötig |
| **Realtime-Kurse** | 10167 / 10091 | nur verzögerte Daten (~15 Min) |
| **Histogramm** (`reqHistogramData`) | leer | ungeklärt, vermutlich abo-abhängig |
Die **Scanner-Parameter** (762 scanCodes) lassen sich zwar abrufen, aber nur als Metadaten jeder
Scanner-Lauf wird abgelehnt.
### Drei Konsequenzen, die den Zuschnitt bestimmen
1. **Keine fundamentale Titelauswahl.** Kennzahlenbasierte Ansätze (Value, Quality, Growth) sind
ohne Zusatzquelle nicht baubar. Das ist verkraftbar, weil unsere beiden geplanten Module
(CongressTrading, OptionsWheel) ihre Kandidaten ohnehin anders bestimmen.
2. **Keine Termine für Quartalszahlen.** `CalendarReport` ist gesperrt. Damit ist der offene
Punkt 4 des OptionsWheel-Konzepts („Earnings-Sperre") **nicht** über die TWS API lösbar er
braucht eine externe Quelle oder eine manuell gepflegte Liste. Das ist die einzige Stelle, an
der uns eine externe Abhängigkeit ernsthaft fehlt.
Behelf ohne externe Quelle: Ein **IV-Anstieg** vor Quartalszahlen ist messbar. Ein Filter
„keine neuen Legs, wenn die IV des Basiswerts stark über ihrem 30-Tage-Mittel liegt" fängt
Earnings indirekt mit ab unschärfer, aber ohne Fremddaten.
3. **Kein Universum-Screening.** Strategien müssen mit einer gepflegten Watchlist auskommen.
---
## 3. Strategiebewertung
### 3.1 Gut umsetzbar Datenlage vollständig
| Strategie | Benötigte Daten | Status |
|---|---|---|
| **Options-Wheel** (Cash-Secured Put → Zuteilung → Covered Call) | Kette, Griechen, IV-Historie, Bestand, Berechtigung | alles vorhanden, [Konzept steht](KONZEPT-Modul-OptionsWheel.md) |
| **Covered Calls auf Bestand** | wie oben, ohne Put-Seite | Teilmenge des Wheel |
| **IV-Rank-gesteuerter Prämienverkauf** | `OPTION_IMPLIED_VOLATILITY` + `HISTORICAL_VOLATILITY` | vorhanden; liefert das Timing-Kriterium für das Wheel |
| **Trendfolge auf Tagesbasis** (gleitende Durchschnitte, Ausbrüche, ATR-Stops) | `ADJUSTED_LAST` Tagesbars, 10 J | vorhanden |
| **Relative Stärke / Dual Momentum** über die Watchlist | Tagesbars mehrerer Titel | vorhanden |
| **Mean Reversion** (RSI, Bollinger, Abstand zum gleitenden Mittel) | Tagesbars | vorhanden |
| **Paar-Handel** korrelierter Titel | Tagesbars, lange Historie | vorhanden |
| **Saisonalität / Kalendereffekte** | 30 Jahre Monatsbars | vorhanden |
| **Sektor-Klumpenrisiko-Prüfung** | Branchenklassifikation | vorhanden; gehört in den `RiskService` |
Allen gemeinsam: Sie entscheiden **auf Schlusskursen oder mit Stunden-/Tagesbezug**. Die
15-Minuten-Verzögerung ist dabei irrelevant, weil die Signale ohnehin aus abgeschlossenen Bars
kommen. Genau deshalb passen sie zu unserer Datenlage.
### 3.2 Bedingt umsetzbar
| Strategie | Einschränkung |
|---|---|
| **Analystenaktionen als Filter oder Signal** (`BRFUPDN`) | Schlagzeilen sind strukturiert und auswertbar. **Ungeprüft**: ob `reqNewsArticle` den Volltext liefert und wie weit die Historie zurückreicht. Als *Risikofilter* („keine neue Position kurz nach einer Abstufung") wertvoller denn als Einstiegssignal. |
| **Volatilitäts-Ausbruch intraday** | Minutenbars gibt es (30 Tage), aber nur verzögert. Rückrechnung möglich, Live-Handel nicht. |
| **Gap-Strategien auf Eröffnung** | Eröffnungskurs kommt verzögert; die Ausführung träfe den Markt 15 Minuten zu spät. Nur mit Realtime-Abo sinnvoll. |
### 3.3 Nicht umsetzbar
| Strategie | Grund |
|---|---|
| Fundamentales Screening (Value, Quality, Growth) | keine Fundamentaldaten |
| Earnings-Strategien (Straddle vor Zahlen, Post-Earnings-Drift) | keine Termine für Quartalszahlen |
| Marktweite Anomalie-Suche / Screener-getriebene Auswahl | Scanner gesperrt |
| Daytrading, Scalping, Orderbuch-Strategien | verzögerte Daten, keine Markttiefe geprüft |
| Nachrichten-Sentiment in der Breite | nur 3 Anbieter, Schlagzeilen |
---
## 4. Empfehlung
**Reihenfolge:**
1. **OptionsWheel** wie im [bestehenden Konzept](KONZEPT-Modul-OptionsWheel.md) bauen. Alle
Voraussetzungen sind jetzt geprüft: Berechtigung, Kette, Griechen mit verzögerten Daten,
Bestandsabgleich. Der einzige offene Punkt ist die Earnings-Sperre dafür zunächst den
IV-Behelf aus Abschnitt 2 einsetzen und die Entscheidung über eine externe Quelle vertagen.
2. **IV-Rank als Core-Baustein** ziehen, nicht als Modul-Interna. Die Kennzahl ist für jede
Prämienstrategie nötig und gehört neben die Kurshistorie in die Datenschicht.
3. **Trendfolge/Momentum auf Tagesbasis** als zweites Modul, wenn ein zweites Standbein gewünscht
ist. Datenlage ist komfortabel, das Risiko liegt in der Strategie, nicht in den Daten.
**Was ein Zusatzabo ändern würde** (Reihenfolge nach Nutzen je Euro):
| Abo | Schaltet frei | Für uns relevant? |
|---|---|---|
| US-Aktien-Realtime (NYSE/AMEX/NASDAQ) | Scanner, Realtime-Kurse | Nur wenn wir Screening oder Intraday wollen. Für Tages- und Prämienstrategien **nicht nötig**. |
| OPRA (Optionen-Realtime) | Realtime-Optionskurse und -Griechen | Verbessert die Ausführungsqualität beim Wheel; für die Strike-Auswahl nicht erforderlich. |
| Refinitiv-Fundamentaldaten | Kennzahlen, Bilanzen, **Termine für Quartalszahlen** | Löst die Earnings-Sperre und öffnet fundamentale Ansätze. Der Kandidat mit dem größten qualitativen Sprung. |
Alle drei sind **Erweiterungen, keine Voraussetzungen**. Der aktuelle Stand trägt die geplanten
Strategien.
---
## 5. Offen / ungeprüft
1. **`reqNewsArticle`** (Volltext zu einer Schlagzeile) nicht getestet. Entscheidet, ob
`BRFUPDN` mehr als ein Ereignis-Marker sein kann.
2. **Wie weit die Nachrichtenhistorie zurückreicht** im Test kamen Meldungen bis 2026-03,
abgefragt waren 30 Tage. Die Zeitfilter-Semantik ist offenbar anders als angenommen und
sollte vor produktiver Nutzung geklärt werden.
3. **Markttiefe** (`reqMktDepth`) nicht getestet, für unsere Strategien vermutlich unnötig.
4. **`reqHistogramData`** liefert leer Ursache ungeklärt (Abo oder Parameter).
5. **Wie zuverlässig verzögerte Griechen außerhalb der Handelszeiten sind** gemessen wurde
während der US-Handelszeit. Außerhalb liefert TWS ggf. eingefrorene Werte.
6. **Ratenbegrenzung** bei Historienabrufen (IBKR drosselt `reqHistoricalData` bei zu vielen
Anfragen). Für einen nächtlichen Watchlist-Abruf relevant, im Test nicht ausgereizt.
+12 -6
View File
@@ -228,13 +228,19 @@ W-0 bis W-3 sind Core-Arbeit und nützen auch den anderen Modulen; erst ab W-4 e
Multiplier 100 über SMART; eine **What-If-Order** auf `AAPL 20260812 C302.5` wurde von IBKR
angenommen (Init-Margin 589,52) statt mit einem Berechtigungsfehler abgelehnt.
Damit ist die Grundvoraussetzung für dieses Modul gegeben.
2. **Greeks bei verzögerten Daten.** `MarketDataType = 4` liefert Optionsberechnungen als verzögerte
Tick-Variante; ob Delta zuverlässig ankommt, muss gegen das laufende Gateway verifiziert werden.
Fällt es aus, greift ersatzweise eine Strike-Wahl über Abstand in % + Mindestprämie der
`StrikeSelector` wird von vornherein so geschnitten, dass beide Kriterien einsetzbar sind.
2. ~~**Greeks bei verzögerten Daten.**~~**erledigt am 2026-08-04, funktioniert.** Gemessen an
`AAPL 20260821 C305`: IV 0,2730 / Delta 0,5443 / Gamma 0,0220 / Vega 0,2626 / Theta 0,2304.
Die Werte kommen über die verzögerten Tick-Felder 8083; **Feld 83 (Modell)** ist die für die
Strike-Wahl maßgebliche Variante. Die delta-basierte Wahl im Band 0,150,30 ist damit umsetzbar,
die Ersatzlösung über prozentualen Abstand wird nicht gebraucht (der `StrikeSelector` behält sie
trotzdem als Rückfalllinie). Details: [KONZEPT-Datenlage-und-Strategien.md](KONZEPT-Datenlage-und-Strategien.md).
3. **Marktdatenabo (OPRA)** für Realtime-Optionskurse Kosten/Notwendigkeit später entscheiden.
4. **Earnings-Sperre**: keine neuen Legs über Quartalszahlen hinweg. Sinnvoll, aber es fehlt eine
Datenquelle für Earnings-Termine Punkt bewusst offen.
4. **Earnings-Sperre**: keine neuen Legs über Quartalszahlen hinweg. **Geprüft am 2026-08-04: über
die TWS API nicht lösbar** `CalendarReport` und alle übrigen Fundamentaldaten sind auf dem
Konto gesperrt (Fehler 10358, Refinitiv-Abo nötig). Das ist die einzige Stelle, an der uns eine
externe Quelle ernsthaft fehlt. **Behelf ohne Fremddaten:** ein IV-Filter keine neuen Legs,
wenn die implizite Volatilität des Basiswerts deutlich über ihrem 30-Tage-Mittel liegt. Fängt
den Earnings-Anstieg indirekt mit ab, unschärfer, aber ohne Abhängigkeit.
5. **Accounting-Anschluss**: Optionsprämien, Zuteilungen und Abrufe müssen im `AccountingClassifier`
eigene Buchungskategorien bekommen; der `RealizedPnlEngine` (FIFO) kennt weder Multiplikator noch
die Einstandsverschiebung durch Zuteilung. Eigene Arbeit im Accounting-Modul.