Options-Fundament und Kapitalmodell im Core; Modul OptionsWheel
Build & Test / build (ubuntu-latest) (push) Canceled after 0s
Build & Test / build (windows-latest) (push) Canceled after 0s

Das OptionsWheel setzt auf Dingen auf, die es im Core nicht gab. Diese
Sitzung baut sie.

#298 Optionen als Instrument. InstrumentKind/OptionSpec und ein
druckbarer Instrumentenschluessel ("AAPL|20260918|C|150"); TradeSignal
und OrderRequest tragen ein optionales OptionSpec, ohne das sich der
Aktienpfad unveraendert verhaelt. Position.Notional rechnet ab jetzt mit
der Kontraktgroesse - vorher war jede Option um Faktor 100 zu klein
bewertet und damit jedes Risikolimit wirkungslos, lautlos und in die
gefaehrliche Richtung. Daneben neu BoundCapital: ein Short Put bindet
Strike x Kontraktgroesse, nicht die vereinnahmte Praemie.

#300 sell-to-open. Jeder Auftrag zerfaellt in einen schliessenden und
einen eroeffnenden Teil; der schliessende wird von keinem Limit
verhindert - auch nicht vom Kaufstopp, denn ein Rueckkauf baut eine
Verpflichtung ab. Deckungspruefung im Core, nicht im Modul: Short Call
nur mit freien Aktien, Short Put nur mit reservierbarem Bargeld.

#302-#305 Kapital- und Buchmodell. Buecher mit reservierten Namen und
Eigentumsregeln, Pool mit Caps und Headroom, Margin-Sperre in vier
Schichten, core_reservation mit Lebenszyklus, Frist, Idempotenz ueber
(SignalId, ContractKey) und Quarantaene beim Start.

#309/#310 Modul OptionsWheel: ow_-Schema mit fuenf Tabellen,
Zustandsautomat, StrikeSelector, RollDecider, PremiumMath - vollstaendig
ohne Broker testbar. Der Handelsbetrieb bleibt gesperrt, solange #299
und #294 fehlen; das Modul sagt das ueber GetActivationBlocker, statt
still nichts zu tun.

#293 --verify-order am Daemon: eine Order durch die vollstaendige Kette
(Buch, Risiko, Bindung, Order, Fill, Buchung, Abschluss). Ohne --confirm
ein Trockenlauf; Abbruch ausserhalb von Paper-Modus UND Paper-Port.

Drei Widersprueche in den Konzepten sind dabei entschieden worden:
Schicht B heisst ab jetzt "kein UNGEDECKTER Short" (der Grund der Regel
war der Kredit, nicht der Short), die Hausreserve wird einmal abgezogen
statt zweimal, und der Positionsschluessel ist eine Spalte statt eines
Sechser-Verbunds (MySQL verlangt NOT NULL fuer Schluesselspalten).
Begruendungen stehen in docs/ROADMAP.md.

Enthaelt ausserdem die noch nicht committete Arbeit aus der vorigen
Sitzung: die Fehlerbehebungen aus Epic X (WorkerBase-Intervall,
BackupWorker, --check gegen die Datenbank, CapitolTrades-429, SIGTERM)
samt DatabaseEndpoint, MysqlDumpTool und der Roadmap-Umstellung. Sie lag
im Arbeitsbaum und liess sich nicht abtrennen, weil beide Staende
dieselben Dateien beruehren.

213 -> 369 Tests, Build ohne Warnung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-04 09:35:38 +02:00
co-authored by Claude Opus 5
parent 9f66183f1c
commit 084cd7ef77
99 changed files with 9947 additions and 445 deletions
+567 -137
View File
@@ -1,31 +1,27 @@
# IBKRTrader – Roadmap
> **Dies ist das einzige Dokument, das sagt, was noch zu tun ist.** Bis zum 2026-08-23 war der
> offene Stand über sieben Konzepte, zwei Referenzdokumente und die Phasen-Checkliste der
> Architektur verteilt; dieselbe Aufgabe stand teils doppelt unter zwei Namen. Diese Roadmap führt
> alles zusammen. Die Quelldokumente bleiben vollständig erhalten und liegen unter
> [archiv/](archiv/) – dort steht das **Warum** und die Herleitung, hier das **Was** und das **Wann**.
> **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.
>
> Regel für die Zukunft: **Ein offener Punkt gehört hierher.** Steht er nur im Konzept, wird er
> vergessen. Wird er erledigt, bekommt er ein ✅ mit Datum – die Zeile bleibt stehen, damit
> nachvollziehbar ist, wann etwas fertig wurde.
> **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.
**Stand: 2026-08-23**
---
## Legende
| Zeichen | Bedeutung |
|---|---|
| ⬜ | Geplant, noch nicht angefangen |
| 🔶 | Angefangen, halbfertig – der genaue Rest steht in der Zeile |
| ✅ | Fertig, mit Datum |
| 🧊 | **Zurückgestellt** – bewusst nicht jetzt, Idee wird vorgehalten. Kein Versehen. |
| ❌ | **Verworfen** – geprüft und entschieden. Nicht erneut vorschlagen, ohne den Grund zu entkräften. |
Die Spalte *Herkunft* nennt die ursprüngliche Kennung im Quelldokument, damit die Herleitung
auffindbar bleibt (z. B. `W-2` im OptionsWheel-Konzept, `P5` im Deploymentcenter-Konzept).
**Übernommen am 2026-08-30** – 57 Einträge, 10 Themenbereiche. Die alte Fassung mit handgepflegten
Statustabellen steht in der Git-Historie (bis Commit `9f66183`).
---
@@ -37,170 +33,590 @@ Linux, kopfloser Daemon, Deploymentcenter-Anbindung. 198 Tests, Build ohne Warnu
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. Alles, was danach kommt (Fill-Buchung,
Kapitalmodell, OptionsWheel), hängt an diesem einen Nachweis.
`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.
---
## Stufe 1 – Fundament schließen
## Die Reihenfolge – warum diese und keine andere
*Diese Punkte blockieren oder gefährden alles Weitere. Sie kommen zuerst.*
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.
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **B1** | **DB-Passwort rotieren.** Das echte Passwort der produktiven MariaDB stand als Vorgabewert in `AppSettings.cs` und liegt damit **in der Git-Historie** (ab Commit `ebeb035`). Der Quelltext trägt seit 2026-08-23 Platzhalter – das ändert an der Historie nichts. Betriebsaktion, nur vom Betreiber ausführbar. | ⬜ | R6, DC-P1 |
| **B2** | **Gitea-Zugangstoken aus der Remote-URL nehmen.** `git remote -v` zeigt den Token im Klartext in der URL; er landet so in jedem Log, jeder Fehlermeldung und jedem Screenshot. Auf Credential-Helper oder SSH umstellen. | ⬜ | Befund 2026-08-23 |
| **B3** | **EF-Schema auf die Datenbank anwenden** (`dotnet ef database update`). Migrationen werden bewusst extern angewendet, nie zur Laufzeit. | ⬜ | R6 |
| **D1** | **CI wieder grün bekommen.** 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. Auflösung: `dotnet pack` im Deploymentcenter-Repo → Gitea-Registry → `PackageReference` statt Cross-Repo-Pfad → CI-Workflow anpassen. | ⬜ | DC-P5, DC-Schritt 10 |
| **H1** | **`PlaceOrderAsync` gegen das Paper-Konto verifizieren** – echte Order, echter Fill, korrekte Buchung. Der einzige Teil des Broker-Adapters, der nie unter realen Bedingungen lief. | ⬜ | R7, IBKR-Integration §Offen 1 |
**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](archiv/Kapital-und-Buchmodell.md) – 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]`).
---
## Stufe 2 – Den Handelspfad belastbar machen
<!-- dc:roadmap stand=2026-09-01T07:48Z items=68 projekt=ibkrtrader -->
*Ohne das ist kein unbeaufsichtigter Betrieb zu verantworten.*
<!-- ERZEUGT - nicht von Hand aendern. Dieser Block wird bei jedem Lauf von
`php cli/roadmap.php --write` ueberschrieben. Der Zustand wird im
Deploymentcenter gepflegt, nicht hier. Prosa gehoert AUSSERHALB der
Marker - dort bleibt sie unberuehrt. -->
| # | Aufgabe | Stand | Herkunft |
### 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 |
|---|---|---|---|
| **H2** | **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.** Nötig: Order-Zustand persistieren (`core_order_state`), `orderStatus`/`execDetails` dauerhaft mitschreiben, Fill nachbuchen, Zustand über einen App-Neustart hinweg rekonstruieren. Ändert den Seam. **Zugleich Voraussetzung für OptionsWheel (dort `W-2`)** – die Aufgabe stand doppelt in zwei Konzepten. | ⬜ | IBKR-Integration, W-2 |
| **H3** | **IBKR-Zugangsdaten mit `EncryptedStringConverter` ablegen.** Der Konverter ist gebaut und getestet, die Credentials nutzen ihn noch nicht. | ⬜ | R7 |
| **H4** | **IBC/IBController einrichten** – IBKR erzwingt 2FA und täglichen Neustart des Gateways. Ohne Auto-Login gibt es keinen unbeaufsichtigten Betrieb. Für Linux zusätzlich kopflos (IBC + Xvfb). Reine Betriebsarbeit, unabhängig vom Code. | ⬜ | L-Ergebnis, IBKR-Integration §Offen 2 |
| **T1** | **Entscheiden, welcher IBKR-Zugangsweg bleibt.** Es gibt zwei parallele: 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. Entweder die Historie auf `reqHistoricalData` umziehen und den REST-Weg aufgeben – oder die Doppelung ausdrücklich begründen. | ⬜ | IBKR-Integration §Offen 4, Befund 2026-08-23 |
| #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 | — |
<!-- /dc:roadmap -->
---
## Stufe 3 – Options-Fundament im Core
## Begründungen
*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.*
*Nicht jedes Vorhaben braucht eine – die meisten sind eine Zeile im Block wert und mehr nicht.
Hier steht, was eine Tabellenzelle nicht trägt.*
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **O1** | `InstrumentKind`/`OptionSpec`, `IbkrMapping.Option`, Positionen mit Multiplikator. Heute rechnet `Position.Notional` als `Quantity × AvgPrice` – bei Optionen um Faktor 100 falsch, womit **jedes Risikolimit wirkungslos wäre**. | ⬜ | W-0 |
| **O2** | Optionskette + Greeks im Core (`reqSecDefOptParams`, `tickOptionComputation`). Gegen das Paper-Gateway bereits nachgemessen: 24 Verfallstermine, 127 Strikes, Greeks auch mit **verzögerten** Daten über die Tick-Felder 80–83 (Feld 83 = Modell ist die maßgebliche Variante). | ⬜ | W-1 |
| **O3** | *(= **H2**, siehe Stufe 2 – dieselbe Aufgabe, hier als Wheel-Voraussetzung geführt)* | ⬜ | W-2 |
| **O4** | Positionsabgleich (Zuteilung & Verfall ändern Positionen **ohne** Order von uns) + `RiskService` um sell-to-open und Deckungsprüfung erweitern. Heute lehnt `EvaluateSell` einen Verkauf ohne Bestand grundsätzlich ab – 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. | ⬜ | W-3 |
| **O5** | **IV-Rank als Core-Baustein**, nicht als Modul-Interna. Die Kennzahl braucht jede Prämienstrategie und sie gehört neben die Kurshistorie in die Datenschicht. | ⬜ | Datenlage §4 |
### [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.
## Stufe 4 – Kapital- und Buchmodell
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.
*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](archiv/Kapital-und-Buchmodell.md) – es ist Referenz, gegen die die
Umsetzung geprüft wird, und war nie umgesetzt.*
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.
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **K1** | **Bücher + Eigentumsregeln.** Jede Position hat genau einen Eigentümer; kein Modul fasst die Position eines anderen oder eine manuell angelegte an. Im Core erzwungen, nicht per Konvention. | ⬜ | Kapitalmodell §2 |
| **K2** | **Ein Pool, Obergrenzen.** Kein Kapitaltransfer zwischen Büchern, niemals aktives Schließen zum Balancieren. Limit-Verletzung ist ein hartes Gate mit strukturiertem Feedback – die Reaktion entscheidet die Strategie, nicht der Core. | ⬜ | Kapitalmodell §3, §6 |
| **K3** | **Margin-Sperre in vier Schichten:** richtige Bemessungsgrundlage (`min(TotalCashValue, AvailableFunds)` − Hausreserve − offene Reservierungen), kein Short, Währungstrennung, Watchdog. Modul-Handel ist strikt cash-only; manueller Handel darf Margin nutzen. | ⬜ | Kapitalmodell §4 |
| **K4** | **Reservierungen über den Order-Lebenszyklus** inkl. Crash-Recovery: beim Start alle nicht-terminalen Reservierungen gegen die offenen Broker-Orders abgleichen. Hängt an **H2**. | ⬜ | Kapitalmodell §5 |
| **K5** | **Abgleich (Reconciliation)** gegen den Broker: `Broker < Ledger` heißt Break – betroffene Bücher und Symbol für neue Orders sperren, Alarm. Unzugeordnete Positionen in Quarantäne, Eskalation nach 30 Minuten mit Backoff. | ⬜ | Kapitalmodell §8, §9 |
| **K6** | **Benachrichtigungen** über Outbox + Sink-Abstraktion. Zielkanal Matrix, Telegram optional. | ⬜ | Kapitalmodell §10 |
| **K7** | **Startwerte festlegen** und in `settings.example.json` dokumentieren: `MaxSymbolPercent`, `HouseReserve`, FX-Haircut, Slippage-Puffer, Reservierungs-TTL, Mindestordergröße je Buch, Handelsplatz-Whitelist je Währung. Beim Bauen zu entscheiden. | ⬜ | Kapitalmodell §15 |
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
## Stufe 5 – Modul OptionsWheel
Zwei getrennte Fehler, die sich gegenseitig verdecken.
*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.*
**#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:
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **W1** | Modul-Gerüst: `IModule`, `ow_`-DbContext + Migration, UI-Tabs, Watchlist. Abschluss: App startet, `--smoke-ui` grün, **kein Handel**. | ⬜ | W-4 |
| **W2** | Reine Strategie-Logik + Tests: `StrikeSelector`, Zustandsautomat je Ticker, `RollDecider`, `PremiumMath`, `CoverageCalculator`. Hohe Testabdeckung ohne Broker. | ⬜ | W-5 |
| **W3** | Verdrahtung + vollautomatischer **Paper**-Betrieb über mehrere Verfallszyklen. Abschluss: mindestens ein vollständiger Wheel-Durchlauf im Paper. | ⬜ | W-6 |
| **W4** | **Earnings-Sperre über den IV-Behelf.** Keine neuen Legs, wenn die IV des Basiswerts deutlich über ihrem 30-Tage-Mittel liegt. Die saubere Lösung (echte Termine für Quartalszahlen) ist über die TWS API **nicht** erreichbar – Fehler 10358, Refinitiv-Abo nötig. Einzige Stelle, an der uns eine externe Quelle ernsthaft fehlt. | ⬜ | W-§7.4, Datenlage §2 |
| **W5** | **Accounting-Anschluss für Optionen:** eigene Buchungskategorien im `AccountingClassifier` für Prämien, Zuteilungen und Abrufe. Der `RealizedPnlEngine` (FIFO) kennt heute weder Multiplikator noch die Einstandsverschiebung durch Zuteilung. Arbeit im Accounting-Modul, nicht im Wheel. | ⬜ | W-§7.5 |
| **W6** | **Live-Freigabe.** Eigene Entscheidung nach W3, kein technischer Schritt. | ⬜ | W-7 |
```
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.
## Laufende Bahnen
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.
*Hängen an keiner Stufe und können jederzeit dazwischen laufen.*
### [X] #391 — `--check` erreicht die Datenbank nie
### Auslieferung / Deploymentcenter
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.
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **D2** | **Projekt `ibkrtrader` im DC-WebUI anlegen** + Token mit `watchdog:ping` und `bugtracker:report`. Serverseitige Handarbeit; ohne sie bleiben Heartbeat, Lizenz und Bugtracker wirkungslos. | ⬜ | DC-Schritt 9 |
| **D3** | **Update-Anwenden verdrahten.** Die Update-**Prüfung** läuft beim Start. `DcUpdateService.LaunchAgent` ist fertig und dokumentiert (`exitCurrentApp` bewusst immer `false`), aber **kein Aufrufer** fährt danach geordnet herunter. Gefundene Updates werden also nie eingespielt. | 🔶 | DC-Schritt 7 |
| **D4** | **Erstes echtes Release fahren.** Pipeline (`scripts/release.*`, `setup.json`) steht, ist aber nie gelaufen. Dabei entsteht `packager.config.json` unter `.dc-tools/`. **Achtung:** Die `preservePatterns` müssen beim *ersten* Release stimmen – ein Update, das `settings.json` überschreibt, nimmt einer laufenden Installation Datenbank, Token und Flex-Zugang gleichzeitig. Ebenso muss der erste Build bereits den `licenseKey` mitgeben, sonst fällt die Tür hinter dem ersten Release zu (bei Predictalytics genau so passiert). | ⬜ | DC-Schritt 8, P6, D2 |
| **D5** | **Bugtracker-Baustein** nach `AGENTS.md` / `.agents/rules`. Braucht **D2**. | ⬜ | DC-Schritt 9 |
| **D6** | **Lizenzfenster für die Shell.** `LicenseGuard` läuft mit `allowPrompt: false` – weder Shell noch Daemon dürfen auf eine Konsoleneingabe warten, die nie kommt. Ein eigenes Fenster für die Desktop-Shell war als Folgeschritt vorgesehen. | ⬜ | DC §5.2 |
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.
### Accounting
### [X] #394 — CapitolTrades antwortet mit 429, und niemand fängt es auf
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **A1** | **Live-IBKR-Flex-Abruf** (Token + Query-Id) und Balance-Anker. Heute liegen dort Offline-Null-Stubs: Das Modul läuft vollständig und bucht dabei korrekt nichts. Ohne diesen Schritt entstehen **keine echten Buchungen** – das Modul ist lauffähig, aber nicht in Betrieb. | ⬜ | Accounting §6 |
| **A2** | **EZB-FX-Ingest** (`acc_fx_rates` füllen) für die EUR-Ansicht. USD als Basis ist sofort verfügbar. | ⬜ | Accounting §6 |
| **A3** | **Steuerschicht.** 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 Jurisdiktionsprofil wird einmalig gesetzt und danach in der DB verankert. **Keine Steuerberatung.** | ⬜ | Accounting §6, Kapitalmodell §11 |
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.
### Supervisor
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.
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **S1** | **Counterfactual-Kursauflösung für Aktien** (späterer Kurs vs. Signalpreis) – Interface und Stub sind vorhanden, die Auflösung fehlt. | ⬜ | Supervisor §offen |
| **S2** | **Externer Versand des Tagesberichts.** Heute nur Persistenz und Log. Sollte denselben Outbox-/Sink-Weg nehmen wie **K6**, statt einen zweiten zu bauen. | ⬜ | Supervisor §offen |
### [X] #396 — Die Shell ignoriert SIGTERM
### Technische Schulden
`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.
| # | Aufgabe | Stand | Herkunft |
|---|---|---|---|
| **T2** | **Log-Level ist nicht konfigurierbar.** `LoggingService.SetMinLevel` ist der einzige Setter und wird nie gerufen; `_minLevel` steht damit fest auf `Info`, und da `Info = 0` der kleinste Wert ist, filtert die Prüfung `level < _minLevel` nie etwas. Entweder an die Einstellungen anbinden oder den Filter aufgeben. | ⬜ | Befund 2026-08-23 |
| **T3** | **Es gibt kein einziges Diagramm.** LiveCharts2 ist in der NuGet-Allowlist vorgesehen und **Avalonia ist deswegen auf der 11er-Linie festgehalten** (11.3.19 / DataGrid 11.3.13). Diese Pinnung kostet uns Avalonia 12, ohne dass bisher ein Diagramm existiert. Entweder mit den neuen Modulen einlösen oder die Pinnung aufgeben. | ⬜ | L-Ergebnis, grundregeln |
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.
### [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.
### [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. Jede Zeile nennt, **was sie wieder aktuell macht**.*
*Bewusste Entscheidungen, keine Versäumnisse. Der Auslöser steht am Item im Deploymentcenter und
wird beim Zurückstellen erzwungen; hier steht, warum nicht jetzt.*
| Idee | Warum nicht jetzt | Wird aktuell, wenn … | Herkunft |
|---|---|---|---|
| **Trendfolge-/Momentum-Modul** auf Tagesbasis als zweites Standbein | Datenlage ist komfortabel (10 J Tagesbars, dividendenbereinigt), das Risiko liegt in der Strategie. Aber ein zweites handelndes Modul vor dem Kapitalmodell wäre fahrlässig. | Wheel im Paper läuft **und** Stufe 4 steht | Datenlage §4 |
| **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. | wir Screening oder Intraday wollen | Datenlage §4 |
| **OPRA-Abo** (Optionen-Realtime) | Verbessert die Ausführungsqualität beim Wheel; für die Strike-Auswahl nachweislich nicht erforderlich (Greeks funktionieren verzögert). | die Ausführungsqualität im Paper-Betrieb messbar stört | W-§7.3 |
| **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. | der IV-Behelf (**W4**) sich als zu unscharf erweist | Datenlage §4 |
| **Self-Cross-Netting** (Modul A kauft, 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. | die Warnung tatsächlich anschlägt | Kapitalmodell §14 |
| **Corporate Actions** (Splits, Spin-offs) in der Buchzuordnung | Vorerst über den Flex-Abgleich als Break sichtbar, manuelle Zuordnung. | ein Break real auftritt | Kapitalmodell §14 |
| **Web-UI** über die interne REST-API | Die REST-API existiert, ein Web-UI ist reine Zusatzarbeit ohne Betriebsnutzen, solange Shell und Daemon reichen. | Fernzugriff nötig wird | grundregeln |
| **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`. | die Shell auf fremden Rechnern laufen soll | release.config.json |
| **Ungeprüfte Datenfragen**: `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. | eine Strategie sie braucht – die Ratenbegrenzung schon bei einem nächtlichen Watchlist-Abruf | Datenlage §5 |
### [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.*
*Damit nichts davon in sechs Monaten erneut als gute Idee auftaucht. Ein neuer Vorschlag braucht
ein Argument, das hier noch nicht steht.*
| Idee | Warum verworfen |
|---|---|
| **Echte IBKR-Sub-Accounts** (Advisor-/Family-Struktur) zur brokerseitigen Trennung der Bücher | Erfordert Kontotypwechsel und feste Vorabaufteilung des Kapitals – deutlich unflexibler als virtuelle Bücher, die denselben Zweck erfüllen. |
| **`InvariantGlobalization=true`** für 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`. |
| **Fundamentales Screening** (Value, Quality, Growth) | Keine Fundamentaldaten über die TWS API. |
| **Earnings-Strategien** (Straddle vor Zahlen, Post-Earnings-Drift) | Keine Termine für Quartalszahlen erreichbar (Fehler 10358). |
| **Marktweite Anomalie-Suche / Screener-getriebene Auswahl** | Der Marktscanner ist ohne Realtime-Abo gesperrt. Jede Strategie arbeitet auf einer gepflegten Watchlist. |
| **Daytrading, Scalping, Orderbuch-Strategien** | Verzögerte Kurse (~15 Min). Ausführung träfe den Markt zu spät. |
| **Gap-Strategien auf Eröffnung** | Eröffnungskurs kommt verzögert – nur mit Realtime-Abo sinnvoll. |
| **Strike-Wahl über 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. |
| **Agent-zu-Agent-Orchestrierung im Supervisor** | Bewusst nicht: Profile sind System-Prompt + Tool-Subset über *einer* Infrastruktur. Einfacher und nachvollziehbarer. |
| **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. |
### [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](ARCHITECTURE.md).*
*Kurzfassung. Die vollständigen Phasen-Checklisten stehen weiterhin in
[ARCHITECTURE.md](ARCHITECTURE.md). Diese Abschnitte wurden bei der Übernahme bewusst **nicht** zu
Items – erledigte Arbeit braucht keinen Bearbeiter.*
| Abschnitt | Inhalt | Fertig |
|---|---|---|
@@ -210,19 +626,20 @@ kein Screening.*
| **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/](archiv/). Sie werden
nicht mehr gepflegt – der Stand steht hier –, aber sie tragen die Herleitung, die Messwerte und die
Begründungen, die eine Roadmap nicht fassen kann.
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](archiv/Kapital-und-Buchmodell.md) | Die vollständige Spezifikation: die drei Wahrheiten, Eigentumsregeln, Reservierungs-Lebenszyklus, Invarianten und Testkatalog. Referenz für Stufe 4. |
| [KONZEPT-Modul-OptionsWheel.md](archiv/KONZEPT-Modul-OptionsWheel.md) | Zustandsautomat, Regelwerk mit Vorgabewerten, `ow_`-Datenmodell, Sicherungen. Referenz für Stufe 5. |
| [Kapital-und-Buchmodell.md](archiv/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](archiv/KONZEPT-Modul-OptionsWheel.md) | Zustandsautomat, Regelwerk mit Vorgabewerten, `ow_`-Datenmodell, Sicherungen. Referenz für `[W]`. |
| [KONZEPT-Datenlage-und-Strategien.md](archiv/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](archiv/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](archiv/KONZEPT-Linux-Portierung.md) | Die Analyse vor dem Umbau: Fundstellenverzeichnis, Aufwandsschätzung und was anders kam als geschätzt. |
@@ -233,3 +650,16 @@ Begründungen, die eine Roadmap nicht fassen kann.
[ARCHITECTURE.md](ARCHITECTURE.md) (Aufbau und Phasen-Historie),
[IBKR-Integration.md](IBKR-Integration.md) (Adapter-Design und Grenzen),
[TWS-Setup-Checkliste.md](TWS-Setup-Checkliste.md) (Einrichtung eines neuen Systems).
---
## Den Block auffrischen
Der erzeugte Block altert. Auffrischen ohne PHP auf dieser Maschine:
```bash
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`.