Files
IBKRTrader/docs/ROADMAP.md
T
RichardandClaude Opus 5 b221e0447b
Build & Test / build (ubuntu-latest) (push) Canceled after 0s
Build & Test / build (windows-latest) (push) Canceled after 0s
Roadmap: Begruendungen zu Waehrungsfaehigkeit, #479 und #480
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 15:50:51 +02:00

42 KiB
Raw Blame History

IBKRTrader – Roadmap

Der Planungsstand steht nicht mehr in dieser Datei. Er liegt seit dem 2026-08-30 im Deploymentcenter, Projekt ibkrtrader – dort, wo auch Fehler und Zuständigkeiten geführt werden. Zwei Pfleger für dasselbe Feld waren der Grund, warum die alte Fassung an drei Stellen behauptete, etwas sei offen, das längst erledigt war.

Die Arbeitsteilung: Status, Reihenfolge, Zielfassung und Bearbeiter kommen aus dem Deploymentcenter (dc_roadmap, WebUI). Begründung, Auslöser und Herleitung stehen hier.

Der Block zwischen den dc:roadmap-Markern ist eine erzeugte Kopie und darf älter sein als der echte Stand. Dort niemals von Hand etwas ändern – der nächste Lauf überschreibt es. Alles außerhalb der Marker ist von Hand und bleibt.

Die Kennung ist die Item-ID aus dem Deploymentcenter (#289), nicht mehr B1 oder W-2. Handvergebene Kennungen wurden nach einer Löschung neu vergeben und meinten dann etwas anderes; eine Item-ID wird nie zweimal vergeben. Der Themenbuchstabe bleibt – aber nur als Gruppe: [B].

Ein neues Vorhaben entsteht immer zuerst im Deploymentcenter (dc_report), dann erscheint es im Block, dann schreibt man die Begründung darunter. Ein Punkt, der nur in dieser Datei steht, ist keine eingeplante Arbeit – er taucht in keiner Arbeitsschlange auf und wird nie übernommen.

Übernommen am 2026-08-30 – 57 Einträge, 10 Themenbereiche. Die alte Fassung mit handgepflegten Statustabellen steht in der Git-Historie (bis Commit 9f66183).


Wo wir stehen

Gebaut und grün: Multi-Projekt-Aufbau nach PolytraderSharp, Generic Host, EF Core, Trading-Kern, drei Module (CongressTrading, Accounting, Supervisor), Avalonia-Oberfläche auf Windows und Linux, kopfloser Daemon, Deploymentcenter-Anbindung. 198 Tests, Build ohne Warnungen.

Nicht gebaut: Es hat noch nie eine echte Order gegeben. Der Broker-Adapter ist gegen das Paper-Konto verifiziert – Verbindung, Konto, Kurse, Optionskette, Greeks, What-If-Order – aber PlaceOrderAsync mit echter Ausführung steht aus (#293). Alles, was danach kommt (Fill-Buchung, Kapitalmodell, OptionsWheel), hängt an diesem einen Nachweis.

Was der erste Betriebstest ergeben hat (2026-08-31). Der Stand 9f66183 wurde auf der Linux-Test-VM gebaut und gefahren, ohne die Testkrücken des Aufbaus – also so, wie er ausgeliefert würde. Build ohne Warnungen, 198 Tests grün, --smoke-ui alle neun Fenster, Launcher und Workers-Ansicht rendern unter Xvfb. Der Dauerlauf hat dann zehn Fehler zutage gefördert, sechs davon in [X]. Der Kern: Was das System über sich selbst meldet, stimmt nicht. Ein toter Worker zeigt einen nächsten Lauf an, ein fehlgeschlagenes Backup meldet „OK", und --check bestätigt eine Datenbank, die es nie kontaktiert hat. Das ist keine Ansammlung von Einzelfehlern, sondern ein Muster – und es trifft ausgerechnet die Anzeigen, an denen man einen unbeaufsichtigten Betrieb ablesen würde.

Der Regelkreis steht seit dem 2026-08-31. Werkbank- und Laufzeit-Token sind ausgestellt (#315), der Heartbeat der Testinstanz läuft (ibkrtrader-linuxtest / linuxtest-vm), und die Detektoren des Deploymentcenters arbeiten darauf. Der Fehler-Stream ist wieder abgeschaltet, bis #403 behoben ist – sonst schreibt der Testaufbau in die Produktivmeldungen.

Zwei Sicherungen sind absichtlich getrennt und beide stehen auf „aus": IBKR.UseTwsApi schaltet den Adapter, TradingEnabled den Handel.


Die Reihenfolge – warum diese und keine andere

Die Rangfolge im Deploymentcenter (roadmap_rank) bildet fünf Stufen ab. Sie stehen hier als Prosa, weil sie eine Begründung sind und keine Statusspalte.

Stufe 0 – Was im Betrieb nachweislich nicht trägt. Themenbereich [X], Rang 1 bis 6. Diese Punkte stammen nicht aus einer Planung, sondern aus einem Testlauf gegen den ausgelieferten Stand auf der Linux-VM am 2026-08-31. Sie stehen vor Stufe 1, weil sie die Stufen darunter unprüfbar machen: Ein Worker, der still stirbt, und ein Backup, das Erfolg meldet, ohne eines zu sein, verfälschen jede spätere Messung. Solange sie offen sind, sagt ein grünes Dashboard nichts aus.

Stufe 1 – Fundament schließen. #289, #290, #291, #292, #293. Diese Punkte blockieren oder gefährden alles Weitere. Zwei davon sind Sicherheitsschulden, die durch Warten nur teurer werden: ein Passwort und ein Token, die beide bereits in einer Historie stehen.

Stufe 2 – Den Handelspfad belastbar machen. #294, #295, #296, #297. Ohne das ist kein unbeaufsichtigter Betrieb zu verantworten.

Stufe 3 – Options-Fundament im Core. #298 bis #301. Reine Core-Arbeit, nützt allen Modulen, nicht nur dem Wheel. Der Aktienpfad bleibt unverändert – neue Felder sind optional, Kind = Stock ist der Default.

Stufe 4 – Kapital- und Buchmodell. #302 bis #308. Wird scharf, sobald ein zweites Modul handelt. Heute handelt nur CongressTrading; mit dem Wheel sind es zwei, die sich ein Konto und ein Guthaben teilen. Das vollständig ausgearbeitete Konzept liegt im Archiv – es ist die Referenz, gegen die die Umsetzung geprüft wird, und war nie umgesetzt.

Stufe 5 – Modul OptionsWheel. #309 bis #314. Erst ab hier entsteht das Modul selbst. Vollautomatisch von Anfang an – Sicherungen sind Schalter und Limits, keine Klick-Freigabe. Strike-Wahl delta-basiert im Band 0,15–0,30. Nur Watchlist, kein Screening.

Laufende Bahnen hängen an keiner Stufe und können jederzeit dazwischen laufen: Auslieferung ([D]), Accounting ([A]), Supervisor ([S]), technische Schulden ([T]).


Als Naechstes

  1. [X] #388 — WorkerBase: Intervall ueber 24,85 Tage toetet den Worker still und reisst beim Herunterfahren den Prozess mit
  2. [X] #390 — Fehlgeschlagener DB-Dump gilt als erfolgreicher Backup-Lauf
  3. [X] #389 — BackupWorker sichert eine andere Datenbank als die, mit der die Anwendung arbeitet
  4. [X] #391 — --check meldet "Pruefdurchlauf OK", ohne die Datenbank je zu erreichen

X — Kritische Bugs

Vorhaben Stand Ziel
#388 WorkerBase: Intervall ueber 24,85 Tage toetet den Worker still und reisst beim Herunterfahren den Prozess mit 📋 Offen —
#390 Fehlgeschlagener DB-Dump gilt als erfolgreicher Backup-Lauf 📋 Offen —
#389 BackupWorker sichert eine andere Datenbank als die, mit der die Anwendung arbeitet 📋 Offen —
#391 --check meldet "Pruefdurchlauf OK", ohne die Datenbank je zu erreichen 📋 Offen —
#394 CapitolTrades-Scraper behandelt 429 nicht - beide CT-Worker fallen bei jedem Lauf aus 📋 Offen —
#396 Die Shell reagiert unter Linux nicht auf SIGTERM - kein geordnetes Herunterfahren ausserhalb des Fensters 📋 Offen —

B — Betrieb und Sicherheit

Vorhaben Stand Ziel
#289 DB-Passwort der produktiven MariaDB rotieren 📋 Offen —
#290 Gitea-Zugangstoken aus der Remote-URL nehmen 📋 Offen —
#291 EF-Schema auf die Datenbank anwenden 📋 Offen —

H — Handelspfad (TWS-Broker-Adapter)

Vorhaben Stand Ziel
#293 PlaceOrderAsync gegen das Paper-Konto verifizieren 📋 Offen —
#294 Asynchrone Order- und Fill-Verfolgung 📋 Offen —
#295 IBKR-Zugangsdaten mit EncryptedStringConverter ablegen 📋 Offen —
#296 IBC/IBController einrichten (Auto-Login, taeglicher Gateway-Neustart) 📋 Offen —

O — Options-Fundament im Core

Vorhaben Stand Ziel
#298 InstrumentKind/OptionSpec, IbkrMapping.Option, Positionen mit Multiplikator 📋 Offen —
#299 Optionskette und Greeks im Core (reqSecDefOptParams, tickOptionComputation) 📋 Offen —
#300 Positionsabgleich bei Zuteilung/Verfall und RiskService um sell-to-open erweitern 📋 Offen —
#301 IV-Rank als Core-Baustein 📋 Offen —

K — Kapital- und Buchmodell

Vorhaben Stand Ziel
#302 Buecher und Eigentumsregeln 📋 Offen —
#303 Ein Pool, Obergrenzen 📋 Offen —
#304 Margin-Sperre in vier Schichten 📋 Offen —
#305 Reservierungen ueber den Order-Lebenszyklus inkl. Crash-Recovery 📋 Offen —
#306 Abgleich (Reconciliation) gegen den Broker 📋 Offen —
#307 Benachrichtigungen ueber Outbox und Sink-Abstraktion 📋 Offen —
#308 Startwerte festlegen und in settings.example.json dokumentieren 📋 Offen —
#331 Self-Cross-Netting (Modul A kauft, Modul B verkauft dasselbe Symbol) 🧊 Zurueckgestellt — Ausloeser: die Warnung im Order-Gateway tatsaechlich anschlaegt —
#332 Corporate Actions (Splits, Spin-offs) in der Buchzuordnung 🧊 Zurueckgestellt — Ausloeser: ein Break durch eine Kapitalmassnahme real auftritt —
#336 Echte IBKR-Sub-Accounts (Advisor-/Family-Struktur) zur Trennung der Buecher ❌ Verworfen —

W — Strategiemodule

Vorhaben Stand Ziel
#309 OptionsWheel: Modul-Geruest 📋 Offen —
#310 OptionsWheel: reine Strategie-Logik und Tests 📋 Offen —
#311 OptionsWheel: Verdrahtung und vollautomatischer Paper-Betrieb 📋 Offen —
#312 OptionsWheel: Earnings-Sperre ueber den IV-Behelf 📋 Offen —
#314 OptionsWheel: Live-Freigabe 📋 Offen —
#327 Trendfolge-/Momentum-Modul auf Tagesbasis als zweites Standbein 🧊 Zurueckgestellt — Ausloeser: das OptionsWheel im Paper-Betrieb laeuft UND das Kapital- und Buchmodell (Stufe 4) steht —
#338 Fundamentales Screening (Value, Quality, Growth) ❌ Verworfen —
#339 Earnings-Strategien (Straddle vor Zahlen, Post-Earnings-Drift) ❌ Verworfen —
#340 Marktweite Anomalie-Suche / Screener-getriebene Auswahl ❌ Verworfen —
#341 Daytrading, Scalping, Orderbuch-Strategien ❌ Verworfen —
#342 Gap-Strategien auf Eroeffnung ❌ Verworfen —
#343 Strike-Wahl ueber prozentualen Abstand statt delta-basiert ❌ Verworfen —

D — Auslieferung und Deploymentcenter

Vorhaben Stand Ziel
#292 CI wieder gruen bekommen (SDK als PackageReference statt Cross-Repo-Pfad) 📋 Offen —
#315 Tokens fuer ibkrtrader vervollstaendigen 📋 Offen —
#316 Update-Anwenden verdrahten 📋 Offen —
#317 Erstes echtes Release fahren 📋 Offen —
#318 Deploymentcenter-Baustein in die Projektdatei aufnehmen ✅ Erledigt —
#319 Lizenzfenster fuer die Shell 📋 Offen —
#334 Desktop-Distribution der Shell ueber die Release-Pipeline 🧊 Zurueckgestellt — Ausloeser: die Shell auf fremden Rechnern laufen soll —

A — Modul Accounting

Vorhaben Stand Ziel
#313 Accounting-Anschluss fuer Optionen 📋 Offen —
#320 Live-IBKR-Flex-Abruf und Balance-Anker 📋 Offen —
#321 EZB-FX-Ingest (acc_fx_rates fuellen) 📋 Offen —
#322 Steuerschicht: Jurisdiktionsprofil festlegen 📋 Offen —
#345 Modulzuordnung als Korrektur des Steuerbuchs ❌ Verworfen —

S — Modul Supervisor

Vorhaben Stand Ziel
#323 Counterfactual-Kursaufloesung fuer Aktien 📋 Offen —
#324 Externer Versand des Tagesberichts 📋 Offen —
#344 Agent-zu-Agent-Orchestrierung im Supervisor ❌ Verworfen —

M — Marktdaten und externe Quellen

Vorhaben Stand Ziel
#328 Marktdaten-Abo US-Realtime (NYSE/AMEX/NASDAQ) 🧊 Zurueckgestellt — Ausloeser: wir Screening oder Intraday-Strategien wollen —
#329 OPRA-Abo (Optionen-Realtime) 🧊 Zurueckgestellt — Ausloeser: die Ausfuehrungsqualitaet im Paper-Betrieb messbar stoert —
#330 Refinitiv-Fundamentaldaten 🧊 Zurueckgestellt — Ausloeser: der IV-Behelf der Earnings-Sperre sich als zu unscharf erweist —
#335 Ungepruefte Datenfragen der TWS API klaeren 🧊 Zurueckgestellt — Ausloeser: eine Strategie eine der Fragen braucht - die Ratenbegrenzung schon bei einem naechtlichen Watchlist-Abruf —

T — Technische Schulden und Entscheidungen

Vorhaben Stand Ziel
#297 Entscheiden, welcher IBKR-Zugangsweg bleibt 📋 Offen —
#325 Log-Level ist nicht konfigurierbar 📋 Offen —
#326 Diagramme einloesen oder die Avalonia-11-Pinnung aufgeben 📋 Offen —
#392 Der Daemon schreibt jede SQL-Anweisung von EF Core auf stdout 📋 Offen —
#393 Workers-Ansicht: die Spalte "Aktiv" ist wirkungslos - Worker lassen sich zur Laufzeit nicht abschalten 📋 Offen —
#395 "Letzter Lauf" bleibt bei einem fehlgeschlagenen Worker-Lauf leer 📋 Offen —
#403 Die Umgebung des Fehler-Streams steht fest im Kompilat - jede Release-Fassung meldet "production" 📋 Offen —
#409 --smoke-ui braucht unter Linux doch ein Anzeigegeraet und ist damit nicht CI-tauglich 📋 Offen —
#333 Web-UI ueber die interne REST-API 🧊 Zurueckgestellt — Ausloeser: Fernzugriff auf eine laufende Instanz noetig wird —
#337 InvariantGlobalization=true fuer ein schlankes Linux-Image ❌ Verworfen —

Begründungen

Nicht jedes Vorhaben braucht eine – die meisten sind eine Zeile im Block wert und mehr nicht. Hier steht, was eine Tabellenzelle nicht trägt.

[X] #388 — WorkerBase: Intervall über 24,85 Tage tötet den Worker still

SemaphoreSlim.WaitAsync(TimeSpan) nimmt höchstens int.MaxValue Millisekunden – rund 24,85 Tage. WorkerBase.RunLoopAsync reicht die Restzeit ungeprüft durch. Zwei ausgelieferte Vorgaben liegen darüber: InstrumentSyncWorker mit 43.200 Minuten und CongressHistoryImportWorker mit 365 Tagen.

Das Bemerkenswerte ist nicht der Fehler, sondern seine Unsichtbarkeit. Die Ausnahme fällt weder in den try/catch von RunOnceAsync noch in das catch (OperationCanceledException) der Warteschleife. Der Task faultet, der Worker ist tot, geloggt wird nichts, und die Workers-Ansicht zeigt weiter einen „Nächsten Lauf" an – für den CT-HistoryImport einen im Jahr 2027.

Beim Herunterfahren kommt die gespeicherte Ausnahme zurück: StopAsync awaitet den Task und fängt nur OperationCanceledException. Sie läuft bis in Program.Main durch und beendet den Prozess mit Exitcode 134. Damit erreicht der Daemon weder StopModulesAsync noch die Zeile „IBKRTrader beendet." – unter systemd ist jedes Stoppen ein Fehlschlag. Dreimal nachgestellt, zuletzt nach 4,5 Stunden Dauerlauf.

Die Testkrücken auf der VM (FromDays(7), IntervalMinutes 1440) umgehen genau das und gehören nach der Behebung wieder heraus.

[X] #390 und #389 — Das Backup meldet Erfolg, ohne eines zu sein

Zwei getrennte Fehler, die sich gegenseitig verdecken.

#390 ist der gefährlichere. RunMysqlDumpAsync behandelt einen Exitcode ungleich 0 mit Logger.Warn und kehrt normal zurück; fehlt das Dump-Werkzeug ganz, wird nur „übersprungen" protokolliert. ExecuteAsync läuft durch, core_worker_log trägt Success, die Workers-Ansicht zeigt „OK". Im Log stehen die beiden Zeilen direkt untereinander:

Warn : mariadb-dump Exitcode 2: ... "Unknown server host 'your-db-host'" when trying to connect
Info : Backup abgeschlossen: 2026-08-31_14-30

#389 ist die Ursache dieses konkreten Fehlschlags. Es gibt zwei Datenbank-Konfigurationen: Database:MySqlConnectionString aus appsettings*.json – daran hängen EF und alle drei Module – und AppSettings.Database aus settings.json, aus der sich BackupWorker und DbVersion bedienen. Nichts erzwingt, dass beide dasselbe meinen; die Vorgabe der zweiten ist der Platzhalter your-db-host. Eine Installation kann also monatelang sauber laufen und dabei kein einziges brauchbares Backup haben. Die Grundregeln sagen „Zugangsdaten NUR in settings.json" – die maßgebliche Verbindung liegt tatsächlich woanders.

Reihenfolge mit Absicht: erst #390, dann #389. Ein Worker, der über Erfolg lügt, verdeckt jedes weitere Backup-Problem, auch die, die nach #389 noch kommen.

[X] #391 — --check erreicht die Datenbank nie

Der Schalter ist als Trockenlauf „für Deployment und CI" dokumentiert und soll laut Kommentar die „DB-Verbindungszeichenfolge" prüfen. Er prüft den String, nicht die Verbindung: RunStartupChecks baut den Host, sieht sich Dateirechte an und schaut nach, ob „SslMode" im Text vorkommt. Gegen Server=127.0.0.1;Port=3999;Database=gibtsnicht meldet er === Prüflauf OK === und Exitcode 0.

Zweiter Punkt derselben Sache: Die Warnungen der Startprüfungen gehen nur in die Logdatei. Ein CI-Schritt, der Exitcode und Ausgabe auswertet – der einzige Grund, warum es diesen Schalter gibt – sieht davon nichts.

[X] #394 — CapitolTrades antwortet mit 429, und niemand fängt es auf

Am 2026-08-27 und am 2026-08-31 dasselbe Bild, unabhängig von der Tageszeit: beide CT-Worker scheitern bei jedem Lauf. EnsureSuccessStatusCode wirft, es gibt keinen Backoff, kein Retry-After, keine Pause zwischen den Seiten – und beide Worker starten gleichzeitig gegen dieselbe Seite. Der ScrapeWorker versucht es alle 30 Minuten unverändert erneut.

Damit fördert das erste und bislang einzige Strategiemodul keine Daten. Ob die Sperre an der Abrufrate hängt oder daran, dass die Gegenstelle den Vorgabe-User-Agent erkennt, ist nicht geklärt – das gehört vor jede Änderung an der Frequenz.

[X] #396 — Die Shell ignoriert SIGTERM

StartWithClassicDesktopLifetime blockiert in der Avalonia-Nachrichtenschleife; das geordnete Herunterfahren liegt im finally darum und wird nur erreicht, wenn das letzte Fenster geschlossen wird. Ein POSIX-Signal fängt niemand ab. Zweimal nachgestellt: Nach kill -TERM läuft der Prozess unverändert weiter.

Auf Linux heißt das: Beim Abmelden und beim Herunterfahren wird die Shell nach Ablauf der Frist mit SIGKILL abgeräumt. Modul-Stopp und Host-Stopp laufen nie, offene Einträge in core_worker_log bleiben auf „Running" stehen. Der Daemon macht es richtig – er behandelt SIGTERM über WaitForShutdownAsync; nur die Shell nicht.

[T] #403 — Die Umgebung des Fehler-Streams steht im Kompilat

DcErrorReporter legt sie als Konstante fest: #if DEBUG heißt „development", sonst „production". Die Wahl fällt damit beim Bauen statt beim Betreiben, und ein Testaufbau soll ja gerade das Kompilat prüfen, das ausgeliefert wird. Am 2026-08-31 hat die Test-VM darum drei Fehler als production in den Bugtracker gestellt. Die Umgebungen staging und testing, die der Bugtracker kennt, sind aus der Anwendung heraus gar nicht erreichbar.

[D] #292 — CI wieder gruen bekommen (SDK als PackageReference statt Cross-Repo-Pfad)

Das Deploymentcenter-SDK hängt als Cross-Repo-ProjectReference am Schwester-Repo; .gitea/workflows/build.yml checkt nur IBKRTrader aus, also ist der CI-Lauf seit dem 2026-08-23 rot. Das war die bewusst in Kauf genommene Folge der Interimslösung.

Es kostet mehr als einen roten Haken. Am 2026-08-30 stellte sich heraus, dass die tatsächlich kompilierte SDK-Fassung längst auf 2.6.0 stand, während überall „2.5.1" dokumentiert war – gewandert durch ein git pull im Schwester-Repo, ohne eine einzige Änderung auf unserer Seite. Ein changelog?since=2.5.1 fragt dann nach dem falschen Ausgangspunkt. Ein PackageReference mit fester Versionsnummer beendet beides auf einmal.

[H] #294 — Asynchrone Order- und Fill-Verfolgung

PlaceOrderAsync ist synchron gedacht: Ausführung oder Fehlschlag innerhalb OrderTimeoutSeconds. Eine Limit-Order im Buch oder eine Market-Order außerhalb der Handelszeiten gilt damit als Fehlschlag – ist bei IBKR aber weiter aktiv. Das ist kein Schönheitsfehler, sondern der Fall, in dem unser Bild vom Konto und das echte Konto auseinanderlaufen.

Diese Aufgabe stand doppelt in zwei Konzepten: als Befund der IBKR-Integration und als W-2 im OptionsWheel-Konzept. Es ist dieselbe Arbeit. Sie ändert den Seam, deshalb kommt sie vor Stufe 4 und Stufe 5, nicht parallel dazu.

[O] #298 — InstrumentKind/OptionSpec, IbkrMapping.Option, Positionen mit Multiplikator

Heute rechnet Position.Notional als Quantity × AvgPrice. Bei Optionen ist das um Faktor 100 falsch – womit jedes Risikolimit wirkungslos wäre, und zwar lautlos und in die gefährliche Richtung. Deshalb steht dieser Punkt vor allem anderen im Options-Fundament.

[O] #300 — Positionsabgleich bei Zuteilung/Verfall und RiskService um sell-to-open erweitern

Zuteilung und Verfall ändern Positionen ohne Order von uns – ein Bestand, den niemand gekauft hat. Zugleich lehnt EvaluateSell heute einen Verkauf ohne Bestand grundsätzlich ab, und genau das ist die Kernoperation des Wheels.

Niemals nackt: Short Call nur mit 100 freien Aktien je Kontrakt, Short Put nur mit reserviertem Cash über Strike × 100. Die Prüfung gehört in den Core, damit ein Modulfehler keine ungedeckte Option schreiben kann. Ein Modul, das sich selbst prüft, prüft sich irgendwann nicht mehr.

[O/K] Entscheidungen bei der Umsetzung von #298, #300 und #302–#305

Die Umsetzung am 2026-09-04 hat vier Stellen aufgedeckt, an denen die Konzepte nicht eindeutig waren oder sich widersprachen. Sie sind so entschieden worden:

Schicht B heißt ab jetzt „kein ungedeckter Short". Das Kapitalmodell formuliert §4 Schicht B als „kein Short – Verkäufe werden auf die Buchmenge gekappt, Shorts sind implizit Margin". Für Aktien gilt das unverändert. Für Optionen wäre es gleichbedeutend mit „kein OptionsWheel", denn dessen Kernoperation ist ein verkaufter Kontrakt. Der Grund der Regel war nie der Short an sich, sondern der Kredit: ein Cash-Secured Put mit reservierter Deckung über Strike × Kontraktgröße und ein Covered Call mit gesperrten Aktien nehmen keinen Kredit auf. Die Sperre wird damit nicht gelockert, sondern auf ihren eigentlichen Gegenstand gestellt. Ein nackter Verkauf bleibt unmöglich, und zwar im Core.

Die Hausreserve wird einmal abgezogen, nicht zweimal. §4 Schicht A zieht sie vom freien Bargeld ab, §3 zieht sie in Verteilbar erneut ab. Das ist dieselbe Rücklage, zweimal beschrieben. Der doppelte Abzug hätte bei 5 % Reserve rund ein Zehntel des Kapitals stillgelegt, ohne dass es irgendwo sichtbar gewesen wäre. CapitalSnapshot.HouseReserve steht nur noch zur Anzeige.

Der Positionsschlüssel ist eine Spalte, nicht der Sechser-Verbund. Das OptionsWheel-Konzept sieht (Module, Symbol, Kind, Expiry, Strike, Right) als fachlichen Schlüssel vor. MySQL verlangt für Primärschlüsselspalten NOT NULL; Expiry, Strike und Right sind bei Aktien aber leer, man bräuchte also Platzhalter wie das Datum 1000-01-01. Stattdessen trägt core_position.ContractKey einen druckbaren Instrumentenschlüssel – bei Aktien schlicht das Ticker-Symbol („AAPL"), bei Optionen „AAPL|20260918|C|150". Bestandszeilen ändern sich dadurch inhaltlich nicht; die Migration füllt die Spalte aus Symbol.

Der Reservierungszustand INTENT entfällt. §5 nennt ihn vor RESERVED. Absicht und Reservierung entstehen aber in derselben Transaktion hinter dem globalen Order-Lock – ein Zustand, den niemand je beobachten kann, wäre eine Zeile, die immer leer ist. Wird die Reservierung später zweistufig, gehört er nachgezogen.

Neu hinzugekommen: das Buch DIAG. Der Order-Nachweis (#293) und später das DebugTrading-Modul (#468) platzieren echte Orders. Ihre Positionen dürfen nicht in der Buchführung eines Strategiemoduls landen – sie verfälschten dessen Auslastung, dessen GuV und dessen Konzentration, und zwar dauerhaft, weil die Position echt ist.

[O/K] Währung gehört zur Identität eines Instruments

Am 2026-09-04 stellte sich beim Order-Nachweis heraus, dass das Paper-Konto sein Bargeld in EUR hält und in USD mit −3.751 im Minus steht. Ein USD-Kauf wäre damit ein USD-Kredit gewesen, und Schicht C hat ihn zu Recht abgelehnt. Die Entscheidung fiel gegen ein Aussetzen der Sicherung und für das, was das Problem an der Wurzel löst: Instrumente sind währungsfähig.

Die Währung steht dabei im Instrumentenschlüssel und nicht bloß als Attribut daneben. Derselbe Ticker notiert an mehreren Plätzen — SAP gibt es in Frankfurt in EUR und als ADR in New York in USD, zu deutlich verschiedenen Kursen. Zwei Notierungen als eine Position zu führen, würde Bestand und Deckung vermischen.

USD bleibt implizit (AAPL, nicht AAPL:USD). Andernfalls wäre jede Bestandszeile ab sofort ein anderes Instrument als vorher und aus einer Position würden zwei. Trennzeichen ist der Doppelpunkt, nicht der Punkt — Ticker enthalten Punkte (BRK.B).

[X] #479 — Die Order lief auf die falsche Notierung

Der teuerste Fund des Tages, und einer, den kein Unit-Test hätte finden können. Geprüft, bepreist und reserviert wurde SAP:EUR zu 188,74 EUR. Ausgeführt wurde die SAP-ADR in New York zu 216,68 USD; das USD-Guthaben rutschte um 434 ins Minus.

Damit wurde genau der Margin-Kredit aufgenommen, den Schicht C verhindern soll — an der Sicherung vorbei, weil die Sicherung ein anderes Instrument geprüft hatte als das, was gehandelt wurde. Ohne Fehlermeldung. Die Bücher wiesen anschließend eine EUR-Position zum USD-Preis aus, die es beim Broker nie gab.

Die Ursache war eine nicht gesetzte Eigenschaft: ExecutionService baute die OrderRequest aus Einzelfeldern, und Currency fiel still auf USD zurück. Die Lehre ist allgemeiner als der Fehler: eine neue Dimension an der Identität eines Objekts macht jede Stelle, die dieses Objekt aus Teilen zusammensetzt, zu einer Fehlerquelle. Deshalb wurde nicht die Zuweisung nachgetragen, sondern OrderRequest.For(instrument, …) eingeführt — im Orderpfad lässt sich ein Auftrag nicht mehr aus losen Feldern bauen.

[K] #480 — Das Kapitalmodell rechnet Währungen zusammen

Solange jedes Instrument USD war, war die Frage gegenstandslos. Mit der ersten EUR-Position ist sie es nicht mehr: Pool, Konzentrationslimit und die Summe der offenen Reservierungen addieren Beträge verschiedener Währungen unverändert.

Bei ähnlichen Kursen fällt das nicht auf — genau deshalb ist es gefährlich. Und die Richtung des Fehlers ist nicht einheitlich: bei einer starken Fremdwährung wird die Auslastung unterschätzt, und ein Buch darf mehr aufmachen als es soll. Das Kapitalmodell sieht die Lösung in §7 bereits vor (Buch-Cash und Reservierungen je Währung); umgesetzt ist sie bisher nur für die Reservierungen und für Schicht C. Für die Pool-Rechnung fehlen Umrechnungskurse — die liefert #321.

[H] #293 — Wie der Nachweis geführt wird

--ibkr-check liest nur. Für #293 gibt es deshalb --verify-order SYMBOL am Daemon: es geht durch die vollständige Kette – Buchzuordnung, Risikoprüfung, Kapitalbindung, Order, Fill, Buchung, Abschluss der Bindung – und schreibt hinterher hin, welche Tabellen zu prüfen sind.

Drei Sicherungen, weil der Schalter als Einziger im Repository absichtlich eine echte Order auslöst: ohne --confirm ist er ein Trockenlauf; er verweigert den Dienst außerhalb des Paper-Modus und außerhalb eines Paper-Ports (eines allein sagt nichts – ein Paper-Modus auf einem Live-Port bewegt echtes Geld); und den globalen Handelsschalter übergeht er nicht, sonst wäre der keiner.

Geführt am 2026-09-04 gegen das Paper-Konto DUR371528: Kauf von 2 × SAP:EUR zu 185,86 EUR, alle fünf Tabellen stimmen. Der Nachweis hat dabei vier Fehler gefunden, drei davon für Unit-Tests unerreichbar — die falsche Notierung (#479), eine Bargeldaufschlüsselung, die gar nicht ankam, eine Kursabfrage ohne Währung, und zwei Spalten der Buchführung, die zu eng waren bzw. leer blieben. Das ist der eigentliche Ertrag: der Lauf gegen echte Gegenstellen prüft die Fugen zwischen den Bausteinen, und dort saßen alle vier.

[W] #309/#310 — Was steht, und was der Handelsbetrieb noch braucht

Gerüst und reine Strategie-Logik stehen: ow_-Schema mit fünf Tabellen, Zustandsautomat, StrikeSelector, RollDecider, PremiumMath, Regelwerk – ohne Broker und ohne Datenbank prüfbar. Offen aus #309 bleibt das Modulfenster; ohne Daten hätte es fünf leere Tabs.

OptionsWheelModule.GetActivationBlocker nennt den Grund, warum das Modul noch nicht handelt, statt still nichts zu tun: #311 ist ohne #299 und #294 nicht erreichbar. Der StrikeSelector braucht das Delta aus der Optionskette, und eine limitierte Optionsorder am Mittelkurs füllt regelmäßig erst nach Minuten – mit dem heutigen synchronen Pfad gälte sie als fehlgeschlagen, während sie bei IBKR weiterläuft. Deshalb registriert das Modul noch keine Worker: ein Gerüst, das handelt, ohne den Fill mitzubekommen, wäre schlimmer als keines.

[T] #297 — Entscheiden, welcher IBKR-Zugangsweg bleibt

Es gibt zwei parallele Wege: den TWS-API-Adapter (Trading/Ibkr/, der handelnde Pfad) und IBKRGatewayService (Client Portal REST, versorgt Instrument-Sync, Kurshistorie und den Watchdog-Heartbeat). Zwei Broker-APIs bedeuten zwei Fehlerbilder, zwei Authentifizierungen und zwei Betriebsvoraussetzungen – und im Störfall die Frage, welcher der beiden gerade lügt.

Entweder die Historie auf reqHistoricalData umziehen und den REST-Weg aufgeben – oder die Doppelung ausdrücklich begründen. Ein Entscheidungspunkt, keine Implementierungsaufgabe.

[D] #317 — Erstes echtes Release fahren

Die Pipeline (scripts/release.*, setup.json) steht, ist aber nie gelaufen. Zwei Fallen liegen beim ersten Lauf und lassen sich danach nicht mehr billig korrigieren:

Die preservePatterns müssen sofort stimmen. Ein Update, das settings.json überschreibt, nimmt einer laufenden Installation Datenbank, Token und Flex-Zugang gleichzeitig.

Der erste Build muss bereits den licenseKey mitgeben, sonst fällt die Tür hinter dem ersten Release zu – bei Predictalytics genau so passiert.

[A] #322 — Steuerschicht: Jurisdiktionsprofil festlegen

Die Jurisdiktion (DE-Kapitalertragsteuer / US Form 8949) ist nicht festgelegt. Der neutrale Ledger und die Abrechnung gelten unabhängig davon; die Steuer-Engine ist als klar abgetrennter Platzhalter angelegt. Das Profil wird einmalig gesetzt und danach in der DB verankert. Keine Steuerberatung.

[T] #466 — TWS-Meldungen mit Umlauten kommen falsch dekodiert an

Am ersten Verbindungstag auf dem neuen Testserver stand im Log verzögerte Marktdaten – das Bild, wenn UTF-8-Bytes als Latin-1 gelesen werden. Betroffen sind nur Texte aus der TWS-API; unsere eigenen deutschen Logzeilen im selben Lauf sind sauber, Konsole und Logdatei scheiden als Ursache damit aus.

Die Wirkung ist gering, aber nicht null: Fehlertexte der Gegenstelle sind das, was im Störungsfall gelesen wird, und sie gehen so auch in den Fehler-Stream des Deploymentcenters – ein Text, der schon beim Erfassen beschädigt ist, lässt sich später nicht mehr zuverlässig gruppieren.

Der schnelle Weg ist, TWS auf englische API-Meldungen zu stellen: englische Fehlertexte sind für die Fehlersuche ohnehin die besseren, weil jede Fundstelle im Netz sie so zitiert. Das ist eine Umgehung, keine Lösung – ob das TWS-API-Paket die Meldungen fest mit einer Kodierung liest, bleibt zu prüfen.

[H] #468 — DebugTrading-Modul: Dauerlast auf der Buchungskette

Alles, was den Handel absichert – Modul-Limits, Slippage-Prüfung, Kapitalpool und Reservierungen, Abgleich gegen den Broker, Behandlung manueller Trades, Accounting-Ingest, Zeitstempel über Zeitzonen hinweg – ist heute ausschließlich durch Unit-Tests mit gemockten Gegenstellen belegt. Im laufenden Betrieb gab es fast keine Trades: das einzige Strategiemodul handelt selten und hing zuletzt an der 429-Sperre. Ein Fehler in der Buchungskette fällt so erst auf, wenn eine echte Strategie scharf geht – und das ist der teuerste Zeitpunkt, ihn zu finden.

Das Modul erzeugt deshalb absichtlich Last statt Rendite: kleine Positionen unter 1.000 USD, über Handelsplätze und Währungen gestreut, mit gewürfelter Haltedauer von Minuten bis Tagen, die es selbstständig wieder schließt. Die lange Spanne ist der Punkt – sie prüft, ob offene Positionen Neustarts, Tageswechsel und den nächtlichen TWS-Neustart überstehen.

Warum unter [H] und nicht als eigener Themenbereich: Es ist kein Strategiemodul und darf nie eines werden. Es ist das Prüfwerkzeug der Orderstrecke und hängt an ihr – ohne die gegen das Paper-Konto verifizierte Order (#293) hat es nichts, worauf es aufsetzen könnte.

Die Sicherung, die nicht verhandelbar ist: Startverweigerung, wenn Trading.Mode nicht Paper ist – hart im Modul, nicht als Einstellung. Ein Testmodul, das im Livebetrieb anläuft, wäre der teuerste denkbare Fehler dieses Projekts. Dazu ein eigenes Tagesbudget, eine Obergrenze gleichzeitig offener Testpositionen, ein Notaus und persistente Testpositionen in dbg_position, damit ein Neustart keine Position vergisst.


🧊 Zurückgestellt – vorgehalten, aber nicht jetzt

Bewusste Entscheidungen, keine Versäumnisse. Der Auslöser steht am Item im Deploymentcenter und wird beim Zurückstellen erzwungen; hier steht, warum nicht jetzt.

[W] #327 — Trendfolge-/Momentum-Modul auf Tagesbasis als zweites Standbein

Die Datenlage ist komfortabel: zehn Jahre Tagesbars, dividendenbereinigt. Das Risiko liegt nicht in den Daten, sondern in der Strategie. Aber ein zweites handelndes Modul vor dem Kapitalmodell wäre fahrlässig – zwei Module an einem Konto ohne Eigentumsregeln und Reservierungen ist genau der Zustand, den Stufe 4 verhindert.

[M] #328 — Marktdaten-Abo US-Realtime (NYSE/AMEX/NASDAQ)

Schaltet Scanner und Realtime frei. Für Tages- und Prämienstrategien nicht nötig – unsere Signale kommen aus abgeschlossenen Bars, die 15-Minuten-Verzögerung ist dabei irrelevant.

[M] #329 — OPRA-Abo (Optionen-Realtime)

Verbessert die Ausführungsqualität beim Wheel. Für die Strike-Auswahl nachweislich nicht erforderlich: die Greeks funktionieren mit verzögerten Daten, am 2026-08-04 gegen das Paper-Gateway gemessen.

[M] #330 — Refinitiv-Fundamentaldaten

Der Kandidat mit dem größten qualitativen Sprung: löst die Earnings-Sperre und öffnet fundamentale Ansätze. Kostet aber Geld für ein System, das noch nie eine Order platziert hat. Deshalb wartet er auf den Nachweis, dass der billige Behelf (#312) nicht reicht.

[K] #331 — Self-Cross-Netting (Modul A kauft, Modul B verkauft dasselbe Symbol)

Bei unserer Handelsfrequenz unrealistisch. Billige Vorstufe stattdessen: ist im Order-Gateway eine gegenläufige Order für dasselbe Symbol pending, wird protokolliert und gewarnt – der Lock ist ohnehin da. Das liefert Daten darüber, ob das Problem je real wird, statt es zu vermuten.

[K] #332 — Corporate Actions (Splits, Spin-offs) in der Buchzuordnung

Vorerst über den Flex-Abgleich als Break sichtbar, manuelle Zuordnung. Eine Automatik zu bauen, bevor der erste Fall aufgetreten ist, heißt gegen eine ausgedachte Spezifikation zu entwickeln.

[T] #333 — Web-UI über die interne REST-API

Die REST-API existiert. Ein Web-UI ist reine Zusatzarbeit ohne Betriebsnutzen, solange Shell und Daemon reichen.

[D] #334 — Desktop-Distribution der Shell über die Release-Pipeline

Die Vorlage nimmt genau eine csproj für alle Runtimes, und das Skript bleibt laut Anleitung unverändert. Release-Kandidat ist deshalb IBKRTrader.Daemon.

[M] #335 — Ungepruefte Datenfragen der TWS API klaeren

reqNewsArticle (Volltext), Reichweite der Nachrichtenhistorie, Markttiefe (reqMktDepth), leeres reqHistogramData, Verlässlichkeit verzögerter Greeks außerhalb der Handelszeiten, Ratenbegrenzung bei Historienabrufen. Keine dieser Fragen blockiert die geplanten Strategien – die Ratenbegrenzung wird aber schon bei einem nächtlichen Watchlist-Abruf zur Frage.


❌ Verworfen – geprüft und entschieden

Damit nichts davon in sechs Monaten erneut als gute Idee auftaucht. Ein neuer Vorschlag braucht ein Argument, das hier noch nicht steht.

[K] #336 — Echte IBKR-Sub-Accounts (Advisor-/Family-Struktur) zur Trennung der Buecher

Erfordert einen Kontotypwechsel und eine feste Vorabaufteilung des Kapitals – deutlich unflexibler als virtuelle Bücher, die denselben Zweck erfüllen.

[T] #337 — InvariantGlobalization=true fuer ein schlankes Linux-Image

In der Portierungsanalyse noch empfohlen, wäre ein Fehler gewesen: ohne ICU fällt die Auflösung von Windows-Zeitzonen-IDs aus und die feste de-DE-Formatierung des PDF-Exports kippt auf invariant. Beides lautlos. Der Daemon setzt es ausdrücklich auf false.

[W] #338 — Fundamentales Screening (Value, Quality, Growth)

Keine Fundamentaldaten über die TWS API.

[W] #339 — Earnings-Strategien (Straddle vor Zahlen, Post-Earnings-Drift)

Keine Termine für Quartalszahlen erreichbar (Fehler 10358, Refinitiv-Abo nötig).

[W] #340 — Marktweite Anomalie-Suche / Screener-getriebene Auswahl

Der Marktscanner ist ohne Realtime-Abo gesperrt. Jede Strategie arbeitet auf einer gepflegten Watchlist.

[W] #341 — Daytrading, Scalping, Orderbuch-Strategien

Verzögerte Kurse (~15 Min). Die Ausführung träfe den Markt zu spät.

[W] #342 — Gap-Strategien auf Eroeffnung

Der Eröffnungskurs kommt verzögert – nur mit Realtime-Abo sinnvoll.

[W] #343 — Strike-Wahl ueber prozentualen Abstand statt delta-basiert

Als Ersatzlösung gedacht, falls Greeks mit verzögerten Daten nicht funktionieren. Sie funktionieren (am 2026-08-04 gemessen). Bleibt im StrikeSelector nur als Rückfalllinie.

[S] #344 — Agent-zu-Agent-Orchestrierung im Supervisor

Bewusst nicht: Profile sind System-Prompt + Tool-Subset über einer Infrastruktur. Einfacher und nachvollziehbarer.

[A] #345 — Modulzuordnung als Korrektur des Steuerbuchs

Harte Regel: Das Managementbuch beeinflusst das Steuerbuch niemals. Die Zuordnung ist eine optionale, nicht-autoritative Beistelltabelle – fehlt sie oder ist sie falsch, ändert sich am Steuerergebnis exakt nichts.


Fertig – der Weg bis hierher

Kurzfassung. Die vollständigen Phasen-Checklisten stehen weiterhin in ARCHITECTURE.md. Diese Abschnitte wurden bei der Übernahme bewusst nicht zu Items – erledigte Arbeit braucht keinen Bearbeiter.

Abschnitt Inhalt Fertig
R1–R7 Kurskorrektur auf das PolytraderSharp-Konzept: Multi-Projekt, Generic Host, EF Core, Trading-Kern, CongressTrading-Strategie, Security, Dashboard, TWS-Broker-Adapter 2026-07
S-0–S-4 Supervisor: Datenfundament (Entscheidungsjournal, Order-Events, SignalId, JSONL-Logs), Dossiers, OpenRouter-Agent mit read-only Tools, Berichte, MCP-Light 2026-07-30
Accounting acc_-Schema, append-only Ingest, Klassifizierung, Abrechnung, FX, CSV-/PDF-Export – mit Offline-Null-Stubs 2026-07-30
L0–L6 Linux-Portierung: Avalonia statt WinForms, Betriebszeitzone, kopfloser Daemon, geteiltes Hosting, WinForms vollständig entfernt, Namensgebung bereinigt 2026-08-07
DC 0–8 Deploymentcenter: Lizenz mit Sperrbetrieb, Watchdog-Heartbeat, globale Ausnahmebehandler + Fehler-Stream, Update-Prüfung, setup.json, Release-Pipeline 2026-08-23
Frühjahrsputz Toter Code entfernt, ungenutzte Symbole und Paketmuster raus, Dokumentenstand an die Wirklichkeit angeglichen 2026-08-23
DC-Anbindung Roadmap ins Deploymentcenter übernommen, MCP-Adapter eingerichtet, Baustein A+B in CLAUDE.md (#318) 2026-08-30

Archiv

Die Quelldokumente sind vollständig erhalten und liegen unter archiv/. Sie werden nicht mehr gepflegt – der Stand steht im Deploymentcenter –, aber sie tragen die Herleitung, die Messwerte und die Begründungen, die eine Roadmap nicht fassen kann.

Dokument Was darin steht, das hier fehlt
Kapital-und-Buchmodell.md Die vollständige Spezifikation: die drei Wahrheiten, Eigentumsregeln, Reservierungs-Lebenszyklus, Invarianten und Testkatalog. Referenz für [K].
KONZEPT-Modul-OptionsWheel.md Zustandsautomat, Regelwerk mit Vorgabewerten, ow_-Datenmodell, Sicherungen. Referenz für [W].
KONZEPT-Datenlage-und-Strategien.md Gegen das laufende Paper-Gateway gemessen, nicht aus der Doku übernommen: was die TWS API liefert und welche Strategien das trägt. Grundlage der Zurückgestellt- und Verworfen-Listen.
KONZEPT-Deploymentcenter-Integration.md Die bewussten Abweichungen vom DC-Leitfaden (Sperrbetrieb statt Prozessende, exitCurrentApp: false) samt Begründung, die Befunde P1–P6 und D1–D7.
KONZEPT-Linux-Portierung.md Die Analyse vor dem Umbau: Fundstellenverzeichnis, Aufwandsschätzung und was anders kam als geschätzt.
KONZEPT-Modul-Accounting.md Leitprinzipien (unabhängige Quelle, Idempotenz, append-only), Datenbeschaffung über Flex Query.
KONZEPT-Modul-Supervisor.md S-0 bis S-4 im Detail, Tool-Registry, Sicherheitsgrenzen des Agenten.

Weiter gepflegt werden – sie sind Referenz, keine Planung: ARCHITECTURE.md (Aufbau und Phasen-Historie), IBKR-Integration.md (Adapter-Design und Grenzen), TWS-Setup-Checkliste.md (Einrichtung eines neuen Systems).


Den Block auffrischen

Der erzeugte Block altert. Auffrischen ohne PHP auf dieser Maschine:

python scripts/roadmap_block.py

Das Skript holt roadmap.php?action=render und ersetzt den Bereich zwischen den Markern. DC_TOKEN kommt aus der Umgebung oder aus .mcp.json.