Files
IBKRTrader/docs/archiv/KONZEPT-Modul-OptionsWheel.md
T
RichardandClaude Opus 5 9f66183f1c
Build & Test / build (ubuntu-latest) (push) Waiting to run
Build & Test / build (windows-latest) (push) Waiting to run
Eine Roadmap statt sieben Konzepte; Quelldokumente ins Archiv
Der offene Stand lag ueber sieben Konzepte, zwei Referenzdokumente und die
Phasen-Checkliste der Architektur verteilt. Dieselbe Aufgabe stand teils
doppelt unter zwei Namen - die asynchrone Fill-Verfolgung etwa als "bekannte
Grenze" in IBKR-Integration.md und zugleich als W-2 im OptionsWheel-Konzept.
Wer wissen wollte, was als Naechstes ansteht, musste alles neun lesen.

docs/ROADMAP.md fuehrt das zusammen:
  - Fuenf Stufen in Abhaengigkeitsreihenfolge, von "Fundament schliessen" bis
    zum OptionsWheel, dazu vier laufende Bahnen (Auslieferung, Accounting,
    Supervisor, technische Schulden).
  - Jede Zeile traegt ihre Herkunft (W-2, P5, Kapitalmodell 5, ...), damit die
    Herleitung im Archiv auffindbar bleibt.
  - Eigene Abschnitte fuer Zurueckgestelltes und Verworfenes. Zurueckgestellte
    Ideen nennen ausdruecklich, WAS sie wieder aktuell macht; verworfene nennen
    den Grund, damit sie nicht in sechs Monaten erneut vorgeschlagen werden.
  - Erledigtes bleibt als Zeile mit Datum stehen statt zu verschwinden.

Archiv (git erkennt alle sieben als Umbenennung, Historie bleibt):
  docs/konzepte/*         -> docs/archiv/
  docs/Kapital-und-Buchmodell.md -> docs/archiv/
Jedes archivierte Dokument bekommt oben einen Vermerk, warum es erhalten bleibt
und wo der lebende Stand steht. Inhaltlich ist keines veraendert.

Weiter gepflegt werden ARCHITECTURE.md (Aufbau + Phasen-Historie),
IBKR-Integration.md (Adapter-Design und Grenzen) und die TWS-Setup-Checkliste -
das sind Referenzen, keine Planung. Ihre eigenen Offen-Listen verweisen jetzt
mit Roadmap-Kennung dorthin, statt einen zweiten Stand zu fuehren.

Alle 63 relativen Markdown-Links geprueft, keiner tot. Fuenf Pfadverweise in
Code, csproj und systemd-Unit mitgezogen; die Unit zeigte auf die
Portierungsanalyse, die ausdruecklich den Stand VOR dem Umbau beschreibt - jetzt
auf ARCHITECTURE.md. Build 0 Warnungen, 198/198 Tests gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 18:14:51 +02:00

16 KiB
Raw Blame History

📦 Archiviert am 2026-08-23

Dieses Dokument wird nicht mehr gepflegt. Was davon noch offen ist, steht in der Roadmap (Stufe 5, dazu die Core-Voraussetzungen in Stufe 3) dort und nur dort wird der Stand nachgeführt.

Es bleibt erhalten, weil es den Zustandsautomaten, das Regelwerk mit allen Vorgabewerten und die Begruendung enthaelt, warum das Modul nicht additiv auf den heutigen aktienbasierten Core passt. Zum Nachschlagen also weiterhin richtig, als Aufgabenliste nicht mehr.


Konzept: Modul „OptionsWheel" (Covered Call / Cash-Secured Put)

Stand: 2026-08-03 Ziel: Auf einer kleinen Watchlist von Tickern, an deren langfristigen Erfolg wir glauben, systematisch Optionsprämien vereinnahmen Cash-Secured Put → (Zuteilung) → Aktienbestand → Covered Call → (Abruf) → wieder Cash. Vollautomatisch, delta-basierte Strike-Wahl, aktives Rollen.

Festlegungen (2026-08-03, mit dem Betreiber abgestimmt):

  1. Put-Seite ist Cash-Secured Put, nicht Covered Put (kein Leerverkauf der Aktie).
  2. Vollautomatisch von Anfang an Sicherungen sind Schalter und Limits, keine Klick-Freigabe.
  3. Strike-Wahl delta-basiert (Zielband 0,150,30).
  4. Bei drohender Zuteilung wird gerollt, solange das per Netto-Kredit möglich ist.

0. Leitprinzipien

  1. Niemals nackt. Ein Short Call ist nur zulässig mit 100 freien Aktien je Kontrakt, ein Short Put nur mit reserviertem Cash über Strike × 100 je Kontrakt. Diese Deckungsprüfung gehört in den Core-RiskService, nicht ins Modul ein Modulfehler darf keine ungedeckte Option schreiben können.
  2. Der Broker ist die Wahrheit. Zuteilung und Verfall ändern Positionen ohne eine Order von uns. Ohne regelmäßigen Positionsabgleich gegen IBKR läuft die eigene Buchführung zwangsläufig auseinander.
  3. Nur Watchlist. Das Modul handelt ausschließlich explizit eingetragene Ticker. Keine Entdeckung, kein Screening, keine Ausweitung zur Laufzeit.
  4. Jede Entscheidung ist nachlesbar. Kandidatenbewertung, Zustandsübergang und Order gehen über core_decision_journal / core_order_events mit gemeinsamer SignalId dieselbe Forensik-Grundlage, die der Supervisor bereits nutzt.

1. Warum das nicht additiv geht: der Core ist heute aktienbasiert

Stelle Heutiger Stand Konsequenz
IbkrMapping.Stock() „der einzige Instrumententyp, den die Module handeln" Kein Options-Kontrakt (Expiry/Strike/Right/Multiplier) baubar
TradeSignal / OrderRequest nur Symbol + Side + Quantity Ein Signal kann keine Option benennen
Position.Notional Quantity × AvgPrice Bei Optionen um Faktor 100 falsch → Risikolimits wirkungslos
RiskService.EvaluateSell „Verkauf schließt Position", lehnt ohne Bestand ab Sell-to-open wird grundsätzlich abgelehnt die Kernoperation des Moduls
IBrokerClient.PlaceOrderAsync Fill-oder-Fehlschlag innerhalb OrderTimeoutSeconds Limitorder am Mid liegt im Buch → gilt als Fehlschlag, ist aber aktiv (bekannte Grenze, IBKR-Integration.md)
IBrokerClient kein reqPositions, keine Optionskette, keine Greeks Zuteilung/Verfall unsichtbar, Strike-Wahl unmöglich

Das Modul setzt also auf einem Core-Options-Fundament auf, das zuerst gebaut wird. Der Aktienpfad bleibt dabei unverändert (neue Felder sind optional, Kind = Stock ist der Default) CongressTrading darf nicht regressieren.


2. Core-Erweiterungen

2.1 Instrumententyp

public enum InstrumentKind { Stock, Option }
public enum OptionRight   { Call, Put }

public sealed record OptionSpec(
    string      Underlying,
    DateOnly    Expiry,
    decimal     Strike,
    OptionRight Right,
    int         Multiplier = 100);

TradeSignal und OrderRequest bekommen je ein optionales OptionSpec? Option. Ist es null, verhält sich alles exakt wie heute.

2.2 Positionen mit Instrument-Identität

core_position erhält Kind, Expiry, Strike, Right, Multiplier; der fachliche Schlüssel wird (Module, Symbol, Kind, Expiry, Strike, Right). Bestandszeilen sind Kind = Stock mit leeren Optionsfeldern Migration ohne Datenumzug. Notional rechnet ab jetzt mit dem Multiplikator. IPortfolioService bekommt entsprechend überladene Abfragen; die bestehenden Aktien-Signaturen bleiben.

2.3 Optionskette + Greeks

Neuer Seam am IBrokerClient:

Task<OptionChain?>  GetOptionChainAsync(string underlying, DateOnly from, DateOnly to, CancellationToken ct);
Task<OptionQuote?>  GetOptionQuoteAsync(OptionSpec spec, CancellationToken ct);   // + Delta, IV, OpenInterest
Task<IReadOnlyList<BrokerPosition>> GetPositionsAsync(CancellationToken ct);

Umsetzung in IbkrConnection:

  • Kette über reqSecDefOptParams liefert Expiries und Strikes je Underlying ohne Marktdaten und ohne Abo. Das ist der einzige gangbare Einstieg: eine volle Kette hat hunderte Kontrakte, TWS erlaubt aber nur ~100 gleichzeitige Marktdatenzeilen.
  • Vorfilter, dann Quotes: Expiry-Fenster aus dem DTE-Ziel, Strike-Fenster um den Spot (± x %) erst für diese Handvoll Kontrakte reqMktData mit Greeks (tickOptionComputation).
  • IbkrMapping.Option(spec)SecType="OPT", Exchange="SMART", LastTradeDateOrContractMonth als yyyyMMdd, Strike, Right="C"/"P", Multiplier="100", plus TradingClass (nötig, sobald ein Underlying mehrere Klassen führt, z. B. nach Splits oder bei Mini-Optionen). Rein, unit-getestet wie das bestehende IbkrMapping.

2.4 Asynchrone Order- und Fill-Verfolgung

Das ist der teuerste, aber unumgängliche Teil: eine Optionsorder wird limitiert am Mid platziert und füllt oft erst Minuten später oder gar nicht.

  • Neue Tabelle core_order_state: Order-ID, SignalId, Kontrakt, Menge, Limit, Status, gefüllte Menge, Zeitstempel überlebt einen App-Neustart.
  • IBrokerClient.SubmitOrderAsync(...) gibt die Order-ID ohne Warten zurück; PlaceOrderAsync bleibt für den Aktienpfad erhalten (intern: submit + warten).
  • Dauerabo auf orderStatus / openOrder / execDetails statt Slot-je-Order; ein OrderTrackingService (IHostedService) trägt Endstatus nach, ruft dann RecordFillAsync und schreibt core_order_events. Nicht gefüllte Orders werden nach einer Frist neu bepreist oder storniert.
  • Nebennutzen: löst die in IBKR-Integration.md dokumentierte Grenze auch für CongressTrading.

2.5 Positionsabgleich (Zuteilung & Verfall)

ReconcilePositionsWorker (IHostedService, z. B. stündlich und nach Marktschluss) vergleicht reqPositions mit core_position:

  • Aktien tauchen auf / Short Put verschwindet → Zuteilung.
  • Short Call verschwindet, Aktien weg → Abruf (called away).
  • Option verschwindet am Verfallstag ohne Gegenbuchung → wertlos verfallen. Jede Abweichung wird gebucht und ins Entscheidungsjournal geschrieben. Unerklärbare Differenzen (manueller Eingriff in TWS) setzen den betroffenen Ticker auf Halted.

2.6 Risiko: sell-to-open + Deckung

RiskContext bekommt FreeShares (nicht bereits durch andere Short Calls gebunden) und AvailableCashForReservation. Neue Regeln im RiskService:

Fall Regel
Sell-to-open Call FreeShares ≥ 100 × Kontrakte, sonst Ablehnung „ungedeckt"
Sell-to-open Put AvailableCashForReservation ≥ Strike × 100 × Kontrakte, sonst Ablehnung
Buy-to-close zulässig bis zur offenen Short-Menge
Limits MaxTradePercent / MaxPositionPercentPerModule rechnen mit dem Multiplikator

3. Modul IBKRTrader.Modules.OptionsWheel

IModule mit Name = "OptionsWheel", DbPrefix = "ow_", referenziert nur den Core, eigener DbContext, eigenes Fenster, eigene Settings-Sektion analog Accounting/CongressTrading.

3.1 Zustandsautomat je Ticker

Idle ──sell put──► ShortPut ──ITM/DTE──► Rolling ──► ShortPut
  ▲                   │                     │
  │                   │ Zuteilung           └─ kein Netto-Kredit ─► (Zuteilung zulassen)
  │                   ▼
  └── called away ── Stock ──sell call──► ShortCall ──ITM/DTE──► Rolling ──► ShortCall
                       ▲                     │
                       └─ verfallen ─────────┘

Zusätzlich PendingOrder (Order im Buch) und Halted (Sperre nach Fehler/Abweichung, nur manuell lösbar). Der Automat ist rein und unit-getestet er kennt weder Broker noch Datenbank.

3.2 Persistenz (ow_)

Tabelle Inhalt
ow_underlyings Watchlist: Ticker, aktiv, Parameter-Überschreibungen, aktueller Zustand
ow_cycles Ein Wheel-Durchlauf (Start, Ende, vereinnahmte Prämie brutto/netto, Ergebnis, Aktien-Einstand)
ow_legs Jede verkaufte/geschlossene Option: Kontrakt, Prämie, Delta+IV bei Eröffnung, DTE, Status, Roll-Kette
ow_candidates Bewertete Strike-Kandidaten je Scan (auch die verworfenen, mit Grund) Nachvollziehbarkeit
ow_events Zustandsübergänge mit Auslöser

ow_candidates ist bewusst dabei: bei Vollautomatik ist die nicht getroffene Wahl die wichtigste Information für die spätere Fehlersuche und für den Supervisor.

3.3 Reine Logik (Logic/, vollständig unit-getestet)

  • StrikeSelector filtert die Kette: DTE-Fenster, Delta-Zielband, Mindestprämie (absolut und als annualisierte Rendite), maximaler Bid/Ask-Spread, Mindest-Open-Interest. Bewertet die Verbleibenden und begründet jede Verwerfung.
  • RollDecider rollen ja/nein: Trigger, Zielkontrakt, Netto-Kredit-Bedingung.
  • CoverageCalculator welche Aktien/welches Cash sind frei, welche durch offene Shorts gebunden.
  • PremiumMath annualisierte Rendite, Break-even, effektiver Einstand nach Prämien.
  • WheelStateMachine zulässige Übergänge, Ableitung des Zustands aus Positionen.

3.4 Worker

  • WheelScanWorker im Handelszeitfenster: Zustand je Ticker prüfen, ggf. neues Leg eröffnen.
  • WheelManageWorker offene Legs überwachen: rollen, schließen, Orders nachbepreisen.

3.5 UI (ein Fenster, Tabs)

Übersicht (je Ticker: Zustand, offenes Leg, DTE, aktuelles Delta, Prämie vereinnahmt/annualisiert), Watchlist (Ticker + Parameter, Not-Aus je Ticker), Kandidaten (letzter Scan inkl. Verwerfungsgründe), Zyklen/Historie, Log. DB-Zugriff nur auf Interaktion (Smoke-UI-sicher).


4. Regelwerk (Defaults, je Ticker überschreibbar)

Parameter Default Begründung
DTE beim Öffnen 3045 Bestes Verhältnis Zeitwertverfall/Handelskosten
Ziel-Delta 0,20 (Band 0,150,30) ≈ 20 % Zuteilungswahrscheinlichkeit
Roll-Trigger DTE ≤ 21 und (Delta > 0,50 oder ITM) Gamma-Risiko steigt in der letzten Woche stark
Roll-Bedingung nur bei Netto-Kredit Ein Debit-Roll kauft nur Zeit und kostet Geld
Covered-Call-Strike ≥ effektiver Einstand der Aktien Verhindert, dass der Wheel systematisch Verluste realisiert
Max. Kontrakte je Ticker 1 Bewusst klein starten
Max. gebundenes Cash gesamt Anteil des Kontowerts Zweite Grenze über die Core-Limits hinaus
Vorzeitiges Schließen bei Gewinnziel aus Nicht gewählt siehe Hinweis unten

Hinweis zum Gewinnziel: Der übliche Begleiter der Roll-Politik ist, ein Leg bei ~50 % vereinnahmter Prämie zurückzukaufen; das nimmt Gamma-Risiko aus der letzten Woche und ist der Grund, warum viele Roll-Trigger nie greifen. Der Parameter ist vorgesehen (ProfitTargetPercent existiert bereits in TradingSettings), steht per Vorgabe auf „aus" und lässt sich ohne Codeänderung zuschalten.

Zuteilung ist nicht abwählbar. Auch bei konsequentem Rollen teilt IBKR zu amerikanische Optionen können jederzeit ausgeübt werden, beim Call besonders vor dem Ex-Dividenden-Tag, und ein Roll ist nicht immer per Netto-Kredit möglich. Zuteilung ist deshalb ein regulärer Pfad des Automaten, kein Fehler. Genau dafür ist der Positionsabgleich (2.5) Pflicht.


5. Sicherungen

Zu den bestehenden zwei Schaltern (IBKR.UseTwsApi, Trading.TradingEnabled) kommen:

  1. OptionsWheel.Enabled Modulschalter.
  2. Watchlist als Whitelist kein Ticker außerhalb.
  3. Naked-Sperre im Core-RiskService (2.6) wirkt auch, wenn die Modullogik falsch liegt.
  4. Halted je Ticker bei unerklärter Positionsabweichung; Neueröffnungen stoppen, bestehende Legs werden weiter verwaltet.
  5. Kill-Switch „keine Neueröffnungen" laufende Positionen bleiben handhabbar.

6. Phasen

Phase Inhalt Abschlusskriterium
W-0 Core: InstrumentKind/OptionSpec, IbkrMapping.Option, Positionen mit Multiplikator Build + Tests grün, Aktienpfad unverändert
W-1 Core: Optionskette + Greeks (reqSecDefOptParams, tickOptionComputation) Kette eines Watchlist-Tickers gegen das Paper-Gateway abrufbar
W-2 Core: asynchrone Order-/Fill-Verfolgung (core_order_state, OrderTrackingService) Limitorder überlebt Timeout und App-Neustart, Fill wird nachgebucht
W-3 Core: Positionsabgleich + RiskService sell-to-open/Deckung Zuteilung im Paper erkannt und gebucht; ungedeckte Order wird abgelehnt
W-4 Modul-Gerüst: IModule, ow_-DbContext + Migration, UI-Tabs, Watchlist App startet, --smoke-ui grün, kein Handel
W-5 Reine Strategie-Logik + Tests (StrikeSelector, StateMachine, RollDecider, PremiumMath) Hohe Testabdeckung ohne Broker
W-6 Verdrahtung + vollautomatischer Paper-Betrieb, Beobachtung über mehrere Verfallszyklen Mindestens ein vollständiger Wheel-Durchlauf im Paper
W-7 Live-Freigabe Eigene Entscheidung nach W-6

W-0 bis W-3 sind Core-Arbeit und nützen auch den anderen Modulen; erst ab W-4 entsteht das Modul selbst.


7. Bewusst offen / zu klären

  1. Optionsberechtigung im Paper-Konto DUR371528 prüfenerledigt am 2026-08-04, vorhanden. Verifiziert gegen das laufende Gateway (Details siehe IBKR-Integration.md, Abschnitt „Optionen"): reqSecDefOptParams für AAPL liefert 24 Verfallstermine, 127 Strikes, 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.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.
  3. Marktdatenabo (OPRA) für Realtime-Optionskurse Kosten/Notwendigkeit später entscheiden.
  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.
  6. Steuer bleibt wie im Accounting-Konzept unberührt und offen. Keine Steuerberatung.