Eine Roadmap statt sieben Konzepte; Quelldokumente ins Archiv
Der offene Stand lag ueber sieben Konzepte, zwei Referenzdokumente und die
Phasen-Checkliste der Architektur verteilt. Dieselbe Aufgabe stand teils
doppelt unter zwei Namen - die asynchrone Fill-Verfolgung etwa als "bekannte
Grenze" in IBKR-Integration.md und zugleich als W-2 im OptionsWheel-Konzept.
Wer wissen wollte, was als Naechstes ansteht, musste alles neun lesen.
docs/ROADMAP.md fuehrt das zusammen:
- Fuenf Stufen in Abhaengigkeitsreihenfolge, von "Fundament schliessen" bis
zum OptionsWheel, dazu vier laufende Bahnen (Auslieferung, Accounting,
Supervisor, technische Schulden).
- Jede Zeile traegt ihre Herkunft (W-2, P5, Kapitalmodell 5, ...), damit die
Herleitung im Archiv auffindbar bleibt.
- Eigene Abschnitte fuer Zurueckgestelltes und Verworfenes. Zurueckgestellte
Ideen nennen ausdruecklich, WAS sie wieder aktuell macht; verworfene nennen
den Grund, damit sie nicht in sechs Monaten erneut vorgeschlagen werden.
- Erledigtes bleibt als Zeile mit Datum stehen statt zu verschwinden.
Archiv (git erkennt alle sieben als Umbenennung, Historie bleibt):
docs/konzepte/* -> docs/archiv/
docs/Kapital-und-Buchmodell.md -> docs/archiv/
Jedes archivierte Dokument bekommt oben einen Vermerk, warum es erhalten bleibt
und wo der lebende Stand steht. Inhaltlich ist keines veraendert.
Weiter gepflegt werden ARCHITECTURE.md (Aufbau + Phasen-Historie),
IBKR-Integration.md (Adapter-Design und Grenzen) und die TWS-Setup-Checkliste -
das sind Referenzen, keine Planung. Ihre eigenen Offen-Listen verweisen jetzt
mit Roadmap-Kennung dorthin, statt einen zweiten Stand zu fuehren.
Alle 63 relativen Markdown-Links geprueft, keiner tot. Fuenf Pfadverweise in
Code, csproj und systemd-Unit mitgezogen; die Unit zeigte auf die
Portierungsanalyse, die ausdruecklich den Stand VOR dem Umbau beschreibt - jetzt
auf ARCHITECTURE.md. Build 0 Warnungen, 198/198 Tests gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
> ### 📦 Archiviert am 2026-08-23
|
||||
> Dieses Dokument wird **nicht mehr gepflegt**. Was davon noch offen ist, steht in der
|
||||
> [Roadmap](../ROADMAP.md) (Stufe 4) – dort und nur dort wird der Stand nachgeführt.
|
||||
>
|
||||
> Es bleibt erhalten, weil es die vollstaendige Spezifikation ist, gegen die die Umsetzung geprueft wird –
|
||||
> die drei Wahrheiten, Eigentumsregeln, Reservierungs-Lebenszyklus, Invarianten und Testkatalog. Zum Nachschlagen also weiterhin richtig,
|
||||
> als Aufgabenliste nicht mehr.
|
||||
|
||||
---
|
||||
|
||||
# Kapital- und Buchmodell
|
||||
|
||||
> **Status: Konzept (2026-08-03) – noch nicht implementiert.**
|
||||
> Dieses Dokument beschreibt, wie mehrere Strategien gleichzeitig auf *einem* IBKR-Konto mit *einem*
|
||||
> Guthaben arbeiten können, ohne sich gegenseitig zu stören, und wie das gegen die steuerliche
|
||||
> Buchführung abgegrenzt ist. Es dient als Referenz, gegen die die spätere Umsetzung geprüft wird.
|
||||
|
||||
## 0. Problemstellung und Abgrenzung
|
||||
|
||||
TWS lässt sich pro Rechner nur einmal betreiben – wir sind an **ein** Konto mit **einem** Guthaben
|
||||
gebunden. Trotzdem sollen mehrere Strategie-Module parallel handeln. Daraus folgen vier Anforderungen:
|
||||
|
||||
1. Jedes Modul bekommt eine **klar begrenzte Kapitalmenge**, die es binden darf.
|
||||
2. Jede Position hat einen **eindeutigen Eigentümer**. Kein Modul darf die Position eines anderen
|
||||
Moduls oder eine manuell angelegte Position anfassen.
|
||||
3. Das Konto darf durch Modul-Handel **niemals ins Minus oder in Margin** laufen.
|
||||
4. Die **steuerliche Buchführung** ist davon vollständig unabhängig und muss zu 100 % stimmen.
|
||||
|
||||
Nicht Gegenstand dieses Dokuments: Strategielogik, Signalerzeugung, Marktdatenversorgung.
|
||||
|
||||
---
|
||||
|
||||
## 1. Die drei Wahrheiten
|
||||
|
||||
Es gibt drei Datenquellen mit unterschiedlicher Autorität. Sie dürfen sich nicht vermischen.
|
||||
|
||||
| Ebene | Quelle | Autoritativ für | Latenz |
|
||||
|---|---|---|---|
|
||||
| **Steuerbuch** (`acc_`) | Flex Query | Geld, Steuer, GuV – alles gegenüber der Steuerbehörde | T+1 |
|
||||
| **Depot-Ist** | TWS-API-Snapshot | Was *jetzt* real im Konto liegt | Sekunden |
|
||||
| **Managementbuch** (`core_`) | eigene Order-Events + Abgleich | Zuordnung von Positionen zu Büchern | live |
|
||||
|
||||
**Harte Regel:** Das Managementbuch beeinflusst das Steuerbuch **niemals** – weder korrigierend noch
|
||||
ergänzend. Der Accounting-Ingest liest Flex Query, klassifiziert und bucht; ob dabei eine `BookId`
|
||||
bekannt ist, ist ihm gleichgültig. Die Modulzuordnung ist eine *optionale, nicht-autoritative*
|
||||
Beistelltabelle (Flex-Trade-ID ↔ BookId). Fehlt sie oder ist sie falsch, ändert sich am Steuerergebnis
|
||||
exakt nichts.
|
||||
|
||||
Umgekehrt gilt: Das Managementbuch **bezieht** Korrekturen aus dem Steuerbuch (Gebühren, Dividenden,
|
||||
Splits), nie andersherum.
|
||||
|
||||
---
|
||||
|
||||
## 2. Bücher (Books)
|
||||
|
||||
Ein **Buch** ist ein Kapitaltopf mit zugeordneten Positionen. Bücher sind:
|
||||
|
||||
| Buch | Bedeutung | Handelt | Limits gelten |
|
||||
|---|---|---|---|
|
||||
| je Strategie (`CT`, …) | ein Modul, ggf. ein Parametersatz | ja | ja |
|
||||
| `MANUAL` | von Hand in TWS/IBKR angelegt | nein (nur extern) | **nein**, zählt aber mit |
|
||||
| `UNASSIGNED` | am Broker gefunden, keinem Buch zugeordnet | nein | zählt mit |
|
||||
| `HOUSE` | nicht allokierte Reserve, FX, Ausgleichsposten | nein | – |
|
||||
|
||||
**`BookId` ist ein eigenes Konzept, nicht der Modulname.** Default `BookId == Modulname`, aber die
|
||||
Trennung erlaubt später mehrere Bücher desselben Moduls und macht `MANUAL`/`UNASSIGNED`/`HOUSE`
|
||||
sauber modellierbar. Jetzt kostenlos, später teuer nachzurüsten.
|
||||
|
||||
### Eigentumsregeln (im Core erzwungen, nicht per Konvention)
|
||||
|
||||
1. Ein Verkaufssignal von Buch *b* kann **nur Positionen von *b*** reduzieren, gedeckelt auf
|
||||
`Menge(b, Symbol)`. Darüber hinaus wird gekappt – **niemals** wird ein Short erzeugt.
|
||||
2. `MANUAL` und `UNASSIGNED` senden keine Signale und empfangen keine.
|
||||
3. Ein Modul kann seine `BookId` **nicht selbst wählen**. Sie stammt aus der Modul-Registrierung und
|
||||
wird serverseitig gesetzt; `TradeSignal.SourceModule` aus dem Modul ist nicht vertrauenswürdig.
|
||||
4. Module bekommen `IBrokerClient` **nicht** per DI – ausschließlich `IExecutionService`.
|
||||
Sonst ist die gesamte Buchführung umgehbar.
|
||||
5. Ein Kill-Switch/Supervisor darf alles liquidieren – als explizite Core-Autorität mit eigener
|
||||
Konfiguration, standardmäßig unter Ausschluss von `MANUAL`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Kapitalmodell: ein Pool, Obergrenzen
|
||||
|
||||
**Es gibt keinen Kapitaltransfer zwischen Büchern.** Kein Buch-Cash, keine Rebalancing-Läufe, keine
|
||||
Ausgleichsforderungen. Stattdessen ein gemeinsamer Cash-Pool und pro Buch eine Obergrenze:
|
||||
|
||||
```
|
||||
Verteilbar = FreiesCash(gehaircuttet) + Σ Modul-Positionswert − HouseReserve
|
||||
|
||||
Cap(b) = konfigurierter Anteil × Verteilbar (Default: Gleichverteilung)
|
||||
Gebunden(b) = Marktwert der Positionen von b + offene Reservierungen von b
|
||||
Headroom(b) = min( Cap(b) − Gebunden(b), freies Pool-Cash )
|
||||
```
|
||||
|
||||
### Eigenschaften, die bewusst so sind
|
||||
|
||||
- **Gewinne verteilen sich von selbst.** Realisiert ein Buch einen Gewinn, wächst der gemeinsame Pool;
|
||||
da alle Caps aus `Verteilbar` abgeleitet werden, steigt der Headroom **aller** Bücher. Diversifikation
|
||||
ohne einen einzigen Zwangsverkauf.
|
||||
- **Ein Buch darf über seinem Cap liegen.** Steigen die Positionen im Wert, passiert nichts – außer
|
||||
dass nicht mehr nachgekauft werden darf. Der Zustand „über Cap" ist **legal**, wird angezeigt und
|
||||
gemeldet, aber **nie geheilt**. Es wird niemals eine Position geschlossen, nur um eine Zielquote
|
||||
herzustellen.
|
||||
- **`Σ Caps ≤ 100 % − Reserve` wird beim Start validiert.** Sonst könnte ein schnelles Buch ein
|
||||
langsames aushungern („wer zuerst kommt").
|
||||
- **Die Modul-Welt sieht nie den NAV**, sondern nur ihr eigenes Kapital plus freies Cash. Manuelle
|
||||
Positionen – auch kreditfinanzierte – sind eine Black Box, die nur über das freie Cash wirkt.
|
||||
|
||||
### Kapitalführung ≠ Performance-Messung
|
||||
|
||||
Die Kapitalführung ist ein Pool plus Caps. Die GuV **pro Buch** ist eine reine *Auswertung* über die
|
||||
Trade-Historie (für Supervisor und Reporting) und steuert nichts. Diese Entkopplung ist beabsichtigt.
|
||||
|
||||
---
|
||||
|
||||
## 4. Keine Margin – die Sperre
|
||||
|
||||
**Modul-Handel ist strikt cash-only. Manueller Handel darf Margin nutzen.** Der Kontotyp ist
|
||||
Cash-Konto; brokerseitig kann trotzdem Margin verfügbar sein, deshalb ist die Software-Sperre
|
||||
zwingend und testpflichtig.
|
||||
|
||||
### Schicht A – richtige Bemessungsgrundlage
|
||||
|
||||
`BuyingPower` und `AvailableFunds` sind auf einem Margin-Konto bereits gehebelt gerechnet und als
|
||||
Basis unbrauchbar. Maßgeblich ist:
|
||||
|
||||
```
|
||||
FreiesCash = min(TotalCashValue, AvailableFunds) − HouseReserve − Σ offene Reservierungen
|
||||
```
|
||||
|
||||
`TotalCashValue` ist das echte Bargeld. Eröffnest du manuell eine Margin-Position, sinkt es, und die
|
||||
Modul-Kaufkraft schrumpft automatisch mit. Bei `TotalCashValue ≤ 0` ist die Modul-Kaufkraft null –
|
||||
ohne Sonderlogik. Module können einen manuellen Kredit strukturell nicht ausweiten.
|
||||
|
||||
### Schicht B – kein Short
|
||||
|
||||
Verkäufe werden auf die Buchmenge gekappt. Shorts sind implizit Margin.
|
||||
|
||||
### Schicht C – Währung
|
||||
|
||||
Ein Kauf in Währung X darf **nur aus Cash in Währung X** finanziert werden. Andernfalls entsteht ein
|
||||
Sollsaldo in X – ein Margin-Kredit, auch wenn das Konto in Summe positiv aussieht. Buch-Cash und
|
||||
Reservierungen werden deshalb **pro Währung** geführt (siehe Abschnitt 7).
|
||||
|
||||
### Schicht D – Watchdog
|
||||
|
||||
Ein Hosted Service prüft Cash und Margin-Kennzahlen laufend. Bei Unterschreitung: **globaler
|
||||
Kauf-Stopp + Alarm** – und **niemals automatisches Verkaufen** zur Heilung. Ein
|
||||
Selbstheilungs-Amoklauf ist gefährlicher als der Zustand, den er beheben soll.
|
||||
|
||||
---
|
||||
|
||||
## 5. Reservierungen (Order-Lebenszyklus)
|
||||
|
||||
Verhindert, dass zwei Module gleichzeitig dasselbe Geld ausgeben. Persistent in der DB, nicht im RAM.
|
||||
|
||||
```
|
||||
INTENT → RESERVED → SUBMITTED → FILLED | PARTIAL | CANCELLED | REJECTED | EXPIRED
|
||||
```
|
||||
|
||||
- **Reservierungshöhe:** `Menge × Referenzpreis × (1 + Puffer)` + geschätzte Kommission.
|
||||
Puffer klein bei Limit-Orders (das Limit *ist* die Obergrenze), größer bei Market (Slippage),
|
||||
zusätzlich FX-Puffer bei währungsübergreifenden Vorgängen.
|
||||
- **Atomar** gegen Buch-Headroom *und* Pool-Cash, in einer DB-Transaktion hinter einem **globalen
|
||||
Order-Lock**. Bei einer TWS-Verbindung reicht ein einfacher `SemaphoreSlim`; feingranulare Locks
|
||||
wären hier nur eine zusätzliche Fehlerquelle.
|
||||
- **Freigabe** beim Terminalzustand; Differenz zwischen Reservierung und echtem Fill fließt zurück.
|
||||
- **TTL zwingend.** Erreicht eine Reservierung ohne Terminalzustand ihr Ablaufdatum oder den
|
||||
Handelsschluss → **Alarm, keine stille Freigabe**. Eine hängende Order, deren Reservierung
|
||||
freigegeben wird, gibt das Geld zweimal aus.
|
||||
- **Idempotenz** über `SignalId` bzw. eine Client-Order-ID, damit ein Reconnect keine Doppelorder
|
||||
erzeugt.
|
||||
- **Crash-Recovery:** beim Start alle nicht-terminalen Reservierungen gegen die offenen Broker-Orders
|
||||
abgleichen; Waisen → Quarantäne + Alarm.
|
||||
|
||||
---
|
||||
|
||||
## 6. Limits und Feedback an die Module
|
||||
|
||||
### Konzentrationslimit pro Einzelwert
|
||||
|
||||
```
|
||||
SymbolExposure(s) = Σ_b Marktwert(b, s) ← über ALLE Bücher, inkl. MANUAL
|
||||
Prüfung: SymbolExposure(s) + geplanter Kauf ≤ MaxSymbolPercent × NAV
|
||||
```
|
||||
|
||||
- **NAV als Nenner**, nicht der Positionswert – sonst wird das Limit bei viel Cash absurd eng und bei
|
||||
wenig Cash absurd weit.
|
||||
- **`MANUAL` ist von der Prüfung befreit, zählt aber voll in den Zähler.** Hältst du 50 % AAPL von
|
||||
Hand, sind die Module bei AAPL vollständig gesperrt. So gewollt.
|
||||
- **Verkäufe sind nie limitiert.** Ein Limit darf niemals das Schließen einer Position verhindern.
|
||||
|
||||
### Feedback-Kontrakt
|
||||
|
||||
Das Gate ist **hart**. Das Modul entscheidet nur, *wie es auf die Ablehnung reagiert* – nicht, ob das
|
||||
Limit gilt. Ein erneut gesendetes zu großes Signal wird wieder abgelehnt. Der Core trimmt nicht
|
||||
selbstständig; die Entscheidung liegt auf Strategieebene.
|
||||
|
||||
```csharp
|
||||
record LimitBreach(
|
||||
LimitKind Kind, // SymbolConcentration | BookCap | CashAvailable
|
||||
// | FxBuffer | CurrencyNotAllowed | VenueNotAllowed
|
||||
string Scope, // "AAPL" | "CT" | "USD"
|
||||
decimal Current,
|
||||
decimal Limit,
|
||||
int MaxQuantity, // was jetzt noch ginge
|
||||
decimal MaxNotional);
|
||||
```
|
||||
|
||||
Ergänzend eine Vorab-Query `GetHeadroom(book, symbol)`, damit ein gut gebautes Modul gar nicht erst
|
||||
gegen die Wand fährt und seine Order gleich richtig dimensioniert.
|
||||
|
||||
**Drossel:** Ein Modul, das dasselbe Signal wiederholt gegen dieselbe Wand schickt, wird nach N
|
||||
Versuchen pro `SignalId` gedämpft und protokolliert. Sonst produziert eine schlecht geschriebene
|
||||
Strategie Logfluten und TWS-Last.
|
||||
|
||||
---
|
||||
|
||||
## 7. Währungen und Handelsplätze
|
||||
|
||||
**Kontobasiswährung:** konfigurierbar, bei Ersteinrichtung festgelegt, danach gesperrt
|
||||
(siehe Abschnitt 11 – eine Änderung nach dem ersten Trade macht alle historischen Bewertungen,
|
||||
Caps und GuV-Zahlen ungültig).
|
||||
|
||||
- Produktion (US LLC): `USD`
|
||||
- Test/Paper (EU): `EUR`
|
||||
|
||||
### Contract-Auflösung ist Pflicht
|
||||
|
||||
Eine Order mit nacktem Symbol-String über `SMART` kann bei mehrdeutigen Tickern still an einer Börse
|
||||
in einer anderen Währung landen. Deshalb:
|
||||
|
||||
- Vor jeder Order wird der Contract über `reqContractDetails` aufgelöst; `currency`, `exchange`,
|
||||
`primaryExchange` und `conId` werden geprüft.
|
||||
- **Whitelist auf zwei Achsen:** Währung ∈ `AllowedCurrencies` **und** Handelsplatz ∈ konfigurierter
|
||||
Liste. Verstoß → harte Ablehnung, unabhängig vom Modul.
|
||||
- Das Ergebnis wird gecacht; **die `conId` wird in der Position gespeichert.** Sie ist IBKRs
|
||||
eindeutige Instrumenten-ID und deutlich sicherer als ein Ticker.
|
||||
|
||||
### FX-Marge als Sicherheitspuffer, nicht als Exposure-Deckel
|
||||
|
||||
Bei überwiegendem Handel in Kontobasiswährung wäre ein FX-Exposure-Limit meist verletzt und damit
|
||||
nutzlos. Sinnvoll wirkt die Marge an drei anderen Stellen:
|
||||
|
||||
1. **Bewertungs-Haircut:** Fremdwährungs-Cash geht mit Abschlag in `Verteilbar` ein → das eigene
|
||||
Kapital wird nie überschätzt.
|
||||
2. **Reservierungspuffer** bei währungsübergreifenden Käufen, zusätzlich zum Slippage-Puffer.
|
||||
3. **FX-Exposure als Kennzahl mit Alarmschwelle** – sichtbar und meldepflichtig, ohne Handelssperre.
|
||||
|
||||
### Kein Auto-FX im Order-Pfad
|
||||
|
||||
Fehlt Cash in der Zielwährung, scheitert der Modul-Kauf mit `LimitKind.CashAvailable`. Konvertiert
|
||||
würde der Order-Pfad selbst, könnte ein Modul indirekt FX-Kosten und FX-Timing auslösen, und die
|
||||
Reservierungslogik müsste zwei Währungen gleichzeitig sperren. FX bleibt ein expliziter Vorgang
|
||||
auf `HOUSE`-Ebene.
|
||||
|
||||
---
|
||||
|
||||
## 8. Abgleich (Reconciliation)
|
||||
|
||||
Weil jederzeit manuell in TWS eingegriffen werden kann, ist der Abgleich kein Zusatz, sondern das
|
||||
Fundament. Zwei Stufen:
|
||||
|
||||
**Stufe 1 – Ledger ↔ TWS (minütlich):** Handelssicherheit. Stimmen Stückzahlen und Cash?
|
||||
|
||||
| Befund | Bedeutung | Reaktion |
|
||||
|---|---|---|
|
||||
| Broker > Ledger | manuell gekauft, oder ein Fill kam nach | Überschuss → `UNASSIGNED` |
|
||||
| Broker < Ledger | manuell verkauft / Corporate Action | **Break:** betroffene Bücher und Symbol für neue Orders sperren, Alarm |
|
||||
| Cash weicht ab | Gebühren, Zinsen, FX | Differenz gegen `HOUSE`, ab Schwelle Break |
|
||||
|
||||
Bei „Broker < Ledger" wird bewusst **nicht** automatisch korrigiert. Anteiliges Wegkürzen zerstört
|
||||
die Zurechenbarkeit – Halt-and-alert, der Mensch entscheidet.
|
||||
|
||||
**Stufe 2 – Ledger ↔ Flex Query (täglich, nach Ingest):** Qualitätssicherung. Hier kommen Gebühren,
|
||||
Dividenden, Quellensteuer, FX-Differenzen und Splits an – Dinge, die die TWS-API gar nicht oder
|
||||
schlecht liefert.
|
||||
|
||||
**Gebühren gehören dem verursachenden Buch.** Sonst subventioniert `HOUSE` die vieltradenden Module
|
||||
und die Performance-Zahlen lügen. Beim Fill wird geschätzt, beim Flex-Abgleich exakt nachjustiert.
|
||||
|
||||
---
|
||||
|
||||
## 9. Unzugeordnete Positionen und Eskalation
|
||||
|
||||
Zustände: `New → Notified → Acknowledged → Assigned(Book)`, mit `DetectedAt`-Zeitstempel.
|
||||
|
||||
- Ein Watchdog meldet alles, was **länger als 30 Minuten** unbearbeitet liegt.
|
||||
- **Backoff:** nach 30 min, dann 2 h, dann täglich – sonst wird der Kanal unbrauchbar.
|
||||
- **Abgestufte Sperrwirkung:** Eine `UNASSIGNED`-Position **zählt voll** in Exposure und
|
||||
Konzentrationslimit, bremst die Module also automatisch, **sperrt aber nicht hart**. Hart gesperrt
|
||||
wird nur der gefährliche Fall aus Abschnitt 8 (Broker < Ledger).
|
||||
|
||||
---
|
||||
|
||||
## 10. Benachrichtigungen
|
||||
|
||||
Im Code existiert derzeit **kein** Benachrichtigungssystem (die Treffer zu „Notification" im
|
||||
Supervisor-MCP sind JSON-RPC-Notifications, etwas anderes). Sauberer Start.
|
||||
|
||||
```
|
||||
INotificationSink Name, SendAsync(Notification, ct)
|
||||
Notification Severity, Category, Title, Body, DedupKey, Data
|
||||
NotificationService Fan-out über alle registrierten Sinks
|
||||
```
|
||||
|
||||
Drei Eigenschaften müssen von Anfang an drin sein, weil sie später schwer nachzurüsten sind:
|
||||
|
||||
- **Outbox in der DB** (`core_notification`): erst persistieren, dann zustellen, Retry bei Fehler.
|
||||
Solange kein Zielkanal existiert, sammeln sich Meldungen sichtbar an und werden zugestellt, sobald
|
||||
ein Sink da ist. Nichts geht verloren.
|
||||
- **Dedup/Throttling** über `DedupKey` (z. B. `unassigned:AAPL`).
|
||||
- **Severity-Routing:** Info → Log/UI, Warning → Chat, Critical → alle Kanäle.
|
||||
|
||||
Ausbaureihenfolge: `LogSink` + `UiSink` sofort → `MatrixSink`, sobald der Server steht →
|
||||
`TelegramSink` optional als Backup.
|
||||
|
||||
**Matrix** ist reines HTTP, keine Library nötig:
|
||||
`PUT /_matrix/client/v3/rooms/{roomId}/send/m.room.message/{txnId}` mit dem Access-Token eines
|
||||
Bot-Users. Die `txnId` ist Matrix' eingebauter Idempotenz-Schlüssel und passt exakt auf die
|
||||
Outbox-ID. Das Token gehört in `SecretProtection`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Steuerbuch: Jurisdiktionsprofil
|
||||
|
||||
**Pro Deployment gibt es genau eine Jurisdiktion, und sie wechselt nie.** Ein Kontowechsel zwischen
|
||||
Ländern findet nicht statt – die Option „später auch in Deutschland" bedeutet ausdrücklich: anderer
|
||||
Server, andere Datenbank, anderes IBKR-Konto. Daraus folgt: keine Migration, keine
|
||||
Mehrmandantenfähigkeit, kein Umschalten zur Laufzeit.
|
||||
|
||||
Das Profil ist **kein Währungs-Flag**, sondern ein Austausch der gesamten Lot- und
|
||||
Klassifikationslogik:
|
||||
|
||||
| Aspekt | US LLC (Hauptziel) | DE privat (Option) | DE GmbH (Option) |
|
||||
|---|---|---|---|
|
||||
| Berichtswährung | USD, keine Umrechnung | EUR, Umrechnung pro Vorgang | EUR, Umrechnung pro Vorgang |
|
||||
| Lot-Matching | FIFO Standard, Specific ID wählbar | FIFO zwingend | Bilanzierung |
|
||||
| Wash Sale (30 Tage) | **ja**, Basis-Anpassung | kein Äquivalent | kein Äquivalent |
|
||||
| Haltefrist | short/long term ab 1 Jahr | irrelevant | irrelevant |
|
||||
| Verlustverrechnung | Carryforward mit Deckel | getrennte Töpfe | wieder anders |
|
||||
| Quellensteuer | – | Anrechnung nach DBA | Anrechnung |
|
||||
| Ausgabe | 1099-B-Abgleich / K-1 | Anlage KAP | Bilanz/GuV |
|
||||
|
||||
Die **Wash-Sale-Regel** ist die einschneidendste: sie verändert Anschaffungskosten rückwirkend. Eine
|
||||
Engine, die nur FIFO kann, lässt sich dafür nicht nachrüsten. Deshalb muss die Lot-Verwaltung von
|
||||
Anfang an lot-basiert sein und **Basis-Anpassungen als eigene Buchungsart** kennen – auch wenn das
|
||||
erste Profil sie noch nicht nutzt.
|
||||
|
||||
> Die konkreten steuerlichen Ausprägungen (u. a. ob die LLC als Disregarded Entity oder Partnership
|
||||
> behandelt wird, was das Reporting ändert) sind mit dem Steuerberater abzustimmen. Hier steht nur,
|
||||
> was die Software mechanisch abbilden können muss.
|
||||
|
||||
### Profile und Umgebungen
|
||||
|
||||
```
|
||||
Test/Paper (EU) BaseCurrency EUR TaxProfile None
|
||||
Produktion (US) BaseCurrency USD TaxProfile UsLlc
|
||||
Option DE BaseCurrency EUR TaxProfile DePrivat | DeGmbh
|
||||
```
|
||||
|
||||
Das Profil **`None`/`Paper`** führt vollständig Buch, rechnet aber keine Steuer und ist in UI und
|
||||
allen Exporten sichtbar als solches markiert. Deutsche Steuerregeln auf Paper-Trades zu rechnen wäre
|
||||
sinnlos und produziert Zahlen, die jemand später für echt halten könnte.
|
||||
|
||||
### Wo der Lock hingehört
|
||||
|
||||
**Nicht in `appsettings.json`.** Eine Datei lässt sich editieren, und dann rechnet die Engine ab
|
||||
morgen nach anderen Regeln über denselben Bestand – der schlimmstmögliche Fehler, weil er sich still
|
||||
auswirkt.
|
||||
|
||||
Der Lock gehört **in die Datenbank, neben die Daten, die er regiert**: eine Stempelzeile im
|
||||
Accounting-Schema mit Jurisdiktion, Berichtswährung, Kontobasiswährung und Einrichtungszeitpunkt.
|
||||
Beim Start wird die Konfiguration dagegen geprüft; bei Abweichung startet das Accounting-Modul nicht,
|
||||
sondern meldet einen Konflikt. Gesetzt wird der Stempel bei der Ersteinrichtung, geändert nur durch
|
||||
eine leere Datenbank.
|
||||
|
||||
### Was deshalb *nicht* gebaut wird
|
||||
|
||||
Die EZB-Kursanbindung (Kursquelle, Stichtagsregeln, Kursarchivierung) ist für das Hauptziel nicht
|
||||
nötig: USD-Konto, USD-Reporting, keine Umrechnung. Also nur die **Naht** bauen (`IFxRateSource`, im
|
||||
US-Profil eine No-Op), nicht die Implementierung. Wird die DE-Option je gezogen, geschieht das
|
||||
ohnehin auf einem anderen Server – dann wird sie dort ergänzt.
|
||||
|
||||
Falls doch: EZB-Referenzkurse autoritativ, IBKR-Bewertungskurse nur als Kontrollgröße; **verwendeter
|
||||
Kurs, Quelle, Stichtag und Abrufzeitpunkt werden mitgespeichert**, nicht nur der umgerechnete Betrag.
|
||||
|
||||
---
|
||||
|
||||
## 12. Datenmodell-Deltas
|
||||
|
||||
| Heute | Delta |
|
||||
|---|---|
|
||||
| `core_position` (PK `Module`+`Symbol`) | `Module` → `BookId`; **`ConId`, `Currency`, `Exchange` ergänzen** |
|
||||
| `core_budget` (Total/Used/MaxPerTrade) | ersetzen durch `core_book`: Cap-Regel, Flags (ReadOnly, Active), Mindestordergröße |
|
||||
| – | `core_reservation` (Zustandsmaschine, TTL, Währung) |
|
||||
| – | `core_broker_snapshot` + `core_reconciliation_break` |
|
||||
| – | `core_notification` (Outbox) |
|
||||
| – | `core_instrument` (aufgelöste Contracts, `conId`-Cache, Whitelist-Status) |
|
||||
| – | `acc_jurisdiction` (Stempel, Abschnitt 11) |
|
||||
| ~~Buch-Cash, Transfers, Rebalance-Lauf~~ | **entfällt** – ein Pool plus Caps (Abschnitt 3) |
|
||||
| `RiskContext.NetLiquidation` | → `Verteilbar` und `Headroom(b)`, **nicht** NAV |
|
||||
| `IbkrConnection`: `NetLiquidation,AvailableFunds` | + `TotalCashValue`, `SettledCash`, Margin-Kennzahlen, **pro Währung** |
|
||||
| `IBrokerClient` | + `GetPositionsAsync`, + `ResolveContractAsync` |
|
||||
| `ExecutionService` (Check-then-Act ohne Lock) | Order-Gateway davor: Serialisierung + Reservierung |
|
||||
| `ExecutionResult.Reason` (string) | + strukturiertes `LimitBreach` (Abschnitt 6) |
|
||||
|
||||
---
|
||||
|
||||
## 13. Invarianten und Testkatalog
|
||||
|
||||
Die No-Margin-Bedingung wird als **eine ausführbare Invariante** formuliert, die an drei Stellen
|
||||
dieselbe Codebahn nimmt:
|
||||
|
||||
```
|
||||
∀ Währung c: FreiesCash(c) ≥ 0
|
||||
∧ Σ offene Reservierungen(c) ≤ FreiesCash(c)
|
||||
∀ Buch b, Symbol s: Menge(b, s) ≥ 0
|
||||
∀ Symbol s: Σ_b Menge(b, s) = BrokerMenge(s)
|
||||
```
|
||||
|
||||
1. als Assert am Ende **jedes** Trading-Tests (gemeinsamer Testhelfer, nicht pro Test neu geschrieben)
|
||||
2. als Property-Test über zufällige Sequenzen aus Reservierung / Fill / Teilfill / Cancel / Slippage /
|
||||
manuellem Eingriff
|
||||
3. als Laufzeitprüfung im Watchdog – **identische Implementierung**, damit Test und Produktion nicht
|
||||
auseinanderlaufen
|
||||
|
||||
### Pflichtfälle
|
||||
|
||||
| Fall | Erwartung |
|
||||
|---|---|
|
||||
| Zwei parallele Reservierungen, zusammen > freies Cash | zweite abgelehnt |
|
||||
| N nebenläufige Tasks (Stresstest) | Σ Reservierungen nie > freies Cash |
|
||||
| Fill teurer als reserviert, innerhalb Puffer | ok, Restfreigabe korrekt |
|
||||
| Fill jenseits des Puffers | Alarm, Cash bleibt ≥ 0 |
|
||||
| Manuelle Margin-Position taucht auf, Cash < Reserve | alle Modul-Käufe gesperrt |
|
||||
| Cash in Zielwährung = 0, andere Währung vorhanden | abgelehnt – **kein** stiller Fremdwährungskredit |
|
||||
| Verkaufssignal über Buchmenge hinaus | gekappt, kein Short |
|
||||
| Reservierung erreicht TTL | Alarm, **keine** stille Freigabe |
|
||||
| Neustart mit offenen Reservierungen | aus DB rekonstruiert, keine Doppelausgabe |
|
||||
| Modul setzt fremde `BookId` im Signal | serverseitig überschrieben |
|
||||
| Verkaufssignal gegen `MANUAL` | abgelehnt |
|
||||
| Contract löst auf fremde Währung/Börse auf | abgelehnt vor Ordersendung |
|
||||
|
||||
---
|
||||
|
||||
## 14. Bewusst verschoben
|
||||
|
||||
- **Self-Cross-Netting** (Modul A kauft, Modul B verkauft dasselbe Symbol gleichzeitig). Bei der
|
||||
erwarteten Handelsfrequenz unrealistisch und allenfalls eine Ausnahmeerscheinung.
|
||||
**Billige Vorstufe jetzt:** Ist im Order-Gateway eine gegenläufige Order für dasselbe Symbol
|
||||
pending, wird das protokolliert und gewarnt. Der Lock ist ohnehin da – das kostet fast nichts und
|
||||
liefert Daten darüber, ob das Problem je real wird.
|
||||
- **Corporate Actions** (Splits, Spin-offs) in der Buchzuordnung – vorerst über den Flex-Abgleich als
|
||||
Break sichtbar, manuelle Zuordnung.
|
||||
- **Echte Sub-Accounts** bei IBKR (Advisor/Family-Struktur) als brokerseitige Trennung. Erwogen und
|
||||
verworfen: erfordert Kontotypwechsel und feste Vorabaufteilung des Kapitals, deutlich unflexibler
|
||||
als virtuelle Bücher.
|
||||
|
||||
---
|
||||
|
||||
## 15. Offene Punkte
|
||||
|
||||
- **Mindestordergröße** pro Buch: ein Wert in Kontobasiswährung, Fremdwährungsorders per Tageskurs
|
||||
dagegen geprüft. Konkreter Wert beim Bauen festzulegen.
|
||||
- **Konkrete Limitwerte** (`MaxSymbolPercent`, `HouseReserve`, FX-Haircut, Slippage-Puffer, TTL)
|
||||
– Startwerte beim Bauen festzulegen und in `settings.example.json` dokumentieren.
|
||||
- **Handelsplatz-Whitelist**: konkrete Börsenliste je Währung.
|
||||
- **Umsetzungsreihenfolge** – noch nicht besprochen.
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung der getroffenen Entscheidungen
|
||||
|
||||
| # | Entscheidung |
|
||||
|---|---|
|
||||
| 1 | Cash-Konto; Modul-Handel strikt cash-only, manueller Handel darf Margin nutzen |
|
||||
| 2 | Kein Kapitaltransfer zwischen Büchern – ein Pool plus Obergrenzen; **niemals** aktives Schließen zum Balancieren |
|
||||
| 3 | Limit-Verletzung: hartes Gate, strukturiertes Feedback; die Reaktion entscheidet die Strategie |
|
||||
| 4 | Kontobasiswährung USD (Produktion) / EUR (Test), bei Ersteinrichtung gesetzt und danach gesperrt |
|
||||
| 5 | Nur USD und EUR handelbar; Contract-Auflösung mit Währungs- und Handelsplatz-Whitelist ist Pflicht |
|
||||
| 6 | Steuerbuch strikt getrennt, Flex Query als einzige Quelle, Jurisdiktionsprofil einmalig und in der DB verankert |
|
||||
| 7 | Unzugeordnete Positionen: Quarantäne + Eskalation nach 30 Minuten mit Backoff |
|
||||
| 8 | Benachrichtigungen über Outbox + Sink-Abstraktion; Matrix als Zielkanal, Telegram optional |
|
||||
Reference in New Issue
Block a user