diff --git a/CLAUDE.md b/CLAUDE.md index bab20d3..e333d19 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,12 @@ # Predictalytics -**Stand des Projekts, offene Punkte und Altlasten-Entscheidungen: -[`STATUS.md`](STATUS.md)** — dort zuerst nachsehen. +**Zuerst hier nachsehen:** +* [`STATUS.md`](STATUS.md) — wo das Projekt steht, was fertig ist. +* [`ROADMAP.md`](ROADMAP.md) — was ansteht, in welcher Reihenfolge, und welche Konzepte + bewusst zurückgestellt sind. + +Die früheren Plandokumente liegen unter [`docs/archiv/`](docs/archiv/) und werden nicht +mehr gepflegt. Betriebsdokumentation zur Anbindung an das Deployment Center (Lizenz, Watchdog, Updates, Fehler-Stream): [`docs/BETRIEB-Deploymentcenter.md`](docs/BETRIEB-Deploymentcenter.md). diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..18adf65 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,310 @@ +# Predictalytics — Roadmap + +> Angelegt am **2026-08-23**. Führt zusammen, was vorher über neun Plandokumente verstreut +> lag. Jeder Punkt hier ist gegen den Code geprüft — was schon umgesetzt war, steht nicht +> mehr drin. +> +> **Ist-Stand des Projekts:** [`STATUS.md`](STATUS.md). +> **Die alten Dokumente** liegen vollständig unter [`docs/archiv/`](docs/archiv/) und +> werden nicht mehr gepflegt. Sie bleiben als Begründungs- und Detailquelle: wo ein Punkt +> unten knapp bleibt, steht die ausführliche Herleitung dort. Die Verweise sind jeweils +> angegeben. + +## Legende + +| | Bedeutung | +|---|---| +| 🔴 | Dringend — sollte vor allem anderen passieren | +| 🟢 | Eingeplant — wird als Nächstes abgearbeitet | +| 🟡 | Backlog — gewollt, aber noch nicht terminiert | +| 💡 | **Konzept — bewusst nicht eingeplant.** Idee ist durchdacht und aufgehoben, die Umsetzung ist nicht beschlossen | +| 👤 | Liegt beim Nutzer, nicht am Code (SQL, Kontoverwaltung, Entscheidungen) | +| ❌ | Bewusst verworfen — nicht wieder aufgreifen ohne neuen Anlass | + +Innerhalb der Stufen ist die Reihenfolge gedacht, aber nicht bindend. Zwischen den Stufen +schon: jede baut auf der vorigen auf. + +--- + +## Stufe 0 — Sofort + +### 0.1 🔴 👤 OpenRouter-API-Key zurückziehen und neu ausstellen + +`src/Predictalytics.Api/appsettings.json` enthält einen **echten** Schlüssel im Klartext, +versioniert seit Commit `7045002` und damit auf dem Gitea-Server. + +1. Schlüssel bei OpenRouter widerrufen, neuen ausstellen. +2. Neuen Schlüssel **nicht** in die Datei schreiben, sondern als Umgebungsvariable + `OpenRouter__ApiKey` setzen. `WebApplication.CreateBuilder` liest Umgebungsvariablen mit + Vorrang vor `appsettings.json` — dafür ist keine Codeänderung nötig. +3. Erst danach den Platzhalter in `appsettings.json` leeren. + +Den Wert nur aus der Datei zu löschen genügt nicht: der Git-Verlauf behält ihn. Deshalb +führt an Schritt 1 kein Weg vorbei. + +> Herkunft: [`archiv/UMSETZUNGSPLAN.md`](docs/archiv/UMSETZUNGSPLAN.md) B2 — dort noch als „kein akuter Risikofall" eingestuft, was +> sich als falsch herausgestellt hat. + +--- + +## Stufe 1 — Aufräumen abschließen + +### 1.1 🟢 `feature/linux-net10` nach `main` bringen + +`main` steht neun Commits zurück und kennt weder die Avalonia-Shell noch die +Deploymentcenter-Anbindung. Alle Feature-Zweige sind in `feature/linux-net10` aufgegangen. + +Danach löschbar (lokal und auf origin): + +``` +copy-portfolio edge-freshness fingerprint-drift-foundation insider-follow-feed +integration smart-money-comovement smart-money-graph ui-ranglisten-showcases +ops-watchdog-license fix-event-description-truncation +``` + +### 1.2 🟡 Acht Nullable-Warnungen beheben + +`TradeRepository.cs:94-102` (6×), `PolymarketProvider.cs:357`, +`TraderAnalyticsWorker.cs:152`. Bestehen seit Längerem und sind kein Aufräumen, sondern +Logikarbeit — jede will einzeln angesehen werden, ob `null` dort auftreten kann. + +### 1.3 🟡 Startup-Backfill entschärfen + +`PredictalyticsHost.cs:116` lädt bei **jedem Start** alle Märkte samt Event in den +Speicher, um Kategorien neu abzuleiten. Bei wachsendem Bestand belastet das die Startzeit +spürbar. Entweder auf Märkte mit `Category = Other` einschränken, oder einmalig laufen +lassen und über ein Flag abschalten. + +--- + +## Stufe 2 — Portierung abschließen + +Der Code ist plattformneutral gebaut, aber **auf keinem Linux-System je ausgeführt worden**. +Bis das nachgeholt ist, ist „läuft unter Linux" eine Annahme. + +> Details zu allen Punkten: [`docs/archiv/PLAN-Linux-Portierung.md`](docs/archiv/PLAN-Linux-Portierung.md), +> Abschnitte 7 und 8. + +### 2.1 🟢 Erstlauf und Verifikation unter Linux + +Die Reihenfolge ist bewusst so: erst beweisen, dass es läuft, dann Deployment bauen. + +- [ ] Shell auf einem Linux-Desktop starten — alle Schaltflächen, Menüs, Persistenz der Einstellungen +- [ ] Derselbe Build mit `--headless` auf einem Server ohne X11: Start, Lizenz aus der Konfiguration, sauberer Stopp per SIGTERM +- [ ] Worker-Dauerlauf ≥ 24 h gegen die **Dev-DB**, Logvergleich mit einem Windows-Lauf +- [ ] Lauf unter `LANG=de_DE.UTF-8` — Volumina, Preise, PnL gegen die Windows-Referenz prüfen (Kultur-Fallen) +- [ ] Web-UI vollständig durchklicken — case-sensitive Assets sind unter Linux ein echtes Risiko +- [ ] Lizenz: Erstaktivierung, Neustart aus dem Cache, Offline-Gnadenfrist, Revalidierung +- [ ] Watchdog-Heartbeats erscheinen am Deployment Center +- [ ] EF-Migrationen von Null gegen die Dev-DB +- [ ] Egress-Kanäle, falls genutzt (`SourceIp` verhält sich unter Linux anders) + +### 2.2 🟢 Deployment und CI + +- [ ] `dotnet publish -r linux-x64` und `-r win-x64`; bei self-contained das Trimming-Verhalten von Avalonia beachten +- [ ] Linux-Systemabhängigkeiten dokumentieren: `libx11-6`, `libice6`, `libsm6`, `libfontconfig1`, `libharfbuzz0b`, Monospace-Schriften +- [ ] systemd-Unit bzw. Dockerfile für den Headless-Betrieb — es existiert bereits `J:\Softwareprojekte\dockerCompose`, vermutlich anknüpfbar +- [ ] Reverse Proxy (nginx/Caddy) mit TLS vor Kestrel +- [ ] Secrets unter Linux: EnvironmentFile mit `0600` statt Klartext in `settings.json` +- [ ] CI: Build + Test auf Linux **und** Windows +- [ ] `docs/BETRIEB-Deploymentcenter.md` um den Linux-Betrieb ergänzen + +--- + +## Stufe 3 — Analytik schärfen + +### 3.1 🟢 Strategie-Klassifikation reparieren (ehemals A4) + +Der größte fachliche Rückstand. `StrategyType` entsteht aus genau zwei Signalen — +`avgSize > 10000` ergibt `Whale`, `hedgingRate > 30` ergibt `Hedger`, sonst `Bot` oder +`Unknown`. Deshalb landet der **Großteil der Trader strukturell auf `Unknown`**, was den +Wert der Einstufung gegen null gehen lässt. + +Erschwerend: die Logik steht **doppelt** da und die beiden Schreiber überschreiben einander +— `AnalyticsService.cs:418` und `ScoringService.cs:89`. + +- [ ] Zuerst die Doppelung auflösen: **ein** Ort, der `StrategyType` bestimmt +- [ ] Vorhandene, bereits berechnete Signale einspeisen statt neue zu erheben: + `StrategyMetricsCalculator` (Kategorie-Konzentration, Conviction-Edge), + `TraderTraitCalculator` (Traits), `FingerprintSnapshotService` (Kategorie-Mix über Zeit) +- [ ] Ergänzend, falls nötig: Verteilung der Haltedauern, Anteil später vs. früher Einstiege + im Marktleben, Varianz der Ordergrößen +- [ ] Prüfen, ob ein Einzel-Enum reicht oder ob Mehrfachsignale danebengehören + (Enum als Primär-Tag, Zusatzsignale als eigene Felder) +- [ ] Abnahme: der Anteil `Unknown` ist deutlich gesunken + +> Herkunft: [`docs/archiv/UMSETZUNGSPLAN.md`](docs/archiv/UMSETZUNGSPLAN.md) A4. + +### 3.2 🟡 `CopytradingBacktestHarness` anschließen oder verwerfen + +123 Zeilen fertig implementierter Latenz-Sweep (0/10/30/60 s) samt `BacktestResult`, per DI +registriert — und **von niemandem aufgerufen**. Der Sweep würde beziffern, wie viel Alpha +ein Nachahmer bei welcher Verzögerung verliert; das passt genau zum Zweck des Projekts. + +Entscheidung nötig: an einen Endpunkt und die Trader-Detailseite hängen, oder ersatzlos +löschen. Der jetzige Zustand — gepflegt, aber wirkungslos — ist die schlechteste Variante. + +### 3.3 🟡 Track-Record-Länge in den Copyability-Score + +Der Score besteht heute aus Alpha-Verfall (70 %) und Sizing-Konsistenz (30 %). Die +ursprünglich geplante vierte Komponente — Länge und Konsistenz des Track Records — fehlt. +Ein Trader mit drei glücklichen Märkten bekommt denselben Score wie einer mit dreihundert. + +> Herkunft: [`archiv/UMSETZUNGSPLAN.md`](docs/archiv/UMSETZUNGSPLAN.md) A5, letzter Unterpunkt. + +### 3.4 🟡 Edge-Freshness und Strategie-Drift ins Ranking + +Beide Kennzahlen existieren, erscheinen aber nur auf der Detailseite und in Alarmen. In der +Rangliste — dort, wo ausgewählt wird — spielen sie keine Rolle. Ein Master mit abgestandener +Edge steht weiterhin oben. + +### 3.5 🟡 Co-Movement-Graph vorberechnen + +`GET /api/co-movement/graph` rechnet bei jedem Aufruf. Für die Netzwerkansicht ist das +träge und belastet die DB unnötig. In den `ScoringAndAlertsWorker` verlagern und das +Ergebnis ablegen. + +### 3.6 🟡 Restpunkte aus der Kategorie-Erkennung + +- [ ] Gezielt fehlende Event-Tags nachladen für Märkte, die getrackte Trader gehandelt haben + ([`archiv/FIXPLAN-TODO.md`](docs/archiv/FIXPLAN-TODO.md) F5) — der tägliche Sync geschlossener Märkte deckt den Fall + inzwischen weitgehend ab, die Lücke ist klein +- [ ] Zwei fehlende Tests (F6): Event-Tag-Vererbung und Offline-Backfill + +### 3.7 🟡 Sortierpfeile in der Trader-Tabelle + +Die Tabellenköpfe zeigen statisch `↕`, auch in der aktiven Sortierspalte. Reine Politik, +war schon im UI-Plan als „Optional" markiert. + +--- + +## Stufe 4 — Datenhaushalt + +Alles hier läuft über SQL, das **der Nutzer selbst** gegen die Datenbank fährt. Wir liefern +die Anweisungen, führen sie aber nicht aus. + +> Details: [`docs/archiv/FIXPLAN-G-Speicher.md`](docs/archiv/FIXPLAN-G-Speicher.md) (Anhang) +> und [`docs/archiv/UMSETZUNGSPLAN.md`](docs/archiv/UMSETZUNGSPLAN.md) C3. + +### 4.1 👤 🟡 Einmalige DB-Optimierungen + +- [ ] Spaltentypen verkleinern: `TransactionHash`, `MarketId`, `AssetId` sind Hex-/Dezimalstrings in überbreiten Feldern +- [ ] InnoDB-Kompression (`ROW_FORMAT=COMPRESSED`) für `Trades` — schneller Zugewinn, geringes Risiko +- [ ] Partitionierung von `Trades` nach Monat (`ExecutedAt`) prüfen — erlaubt späteres Verwerfen ganzer Partitionen + +### 4.2 👤 💡 Materialitätsschwelle für Mikro-Trades + +Trades unterhalb eines Betrags (Vorschlag: $0,50) unabhängig vom Retention-Fenster +verwerfen. Durchdacht, aber nicht beschlossen — die Schwelle verändert die PnL-Rechnung +leicht, und der Gewinn ist ungewiss, bis 4.1 gemessen ist. + +### 4.3 👤 💡 Frischer Datenbank-Neustart + +Die alte Überlegung, die Produktivdatenbank leer neu aufzusetzen und über die Worker neu zu +befüllen (Rohdaten sind über `/activity` jederzeit nachladbar). **Nicht beschlossen.** Falls +er kommt, gehört er ans Ende von Stufe 3, nicht davor — sonst laufen Analytik-Änderungen und +Datenumzug gleichzeitig. + +Vorgelagerter Bestätigungs-Check: stichprobenartig prüfen, wie weit `/activity` für ein +lange aktives Wallet tatsächlich zurückreicht. + +### 4.4 💡 65 Migrationsdateien zusammenfassen + +32 Migrationen ergeben 65 Dateien und rund 37.000 Zeilen. Eine neue Baseline wäre +übersichtlicher, kostet aber die Schemahistorie und ist gegen eine laufende Produktivdatenbank +heikel. Nur sinnvoll im Zuge von 4.3. + +--- + +## Stufe 5 — Ingest skalieren + +Erst angehen, wenn Rate-Limits tatsächlich drücken. Die wirksamsten Maßnahmen sind bereits +umgesetzt (Ingest-Tiering, Egress-Kanäle, Kurzzeit-Cache). + +> Details: [`docs/archiv/PLAN-DatenIngest-Skalierung.md`](docs/archiv/PLAN-DatenIngest-Skalierung.md). + +### 5.1 💡 Historische Massendaten aus der Blockchain statt über REST + +Der größte ungenutzte Hebel. Polymarket-Trades sind On-Chain-Events auf Polygon; für +Backfill und Deep-Resync — die teuersten REST-Verbraucher — ist `/activity` die falsche +Quelle. Ein GraphQL-Aufruf gegen einen Subgraph (The Graph / Goldsky) liefert tausende Fills +eines Wallets auf einmal. + +Skizze: `IHistoricalTradeSource` neben den REST-Provider stellen, Deep-Resync zieht darüber. +**Nicht eingeplant**, weil erst zu evaluieren ist, ob ein brauchbarer Subgraph existiert und +wie vollständig er ist. + +### 5.2 🟡 Markt-Stammdaten aggressiv cachen + +Metadaten (Frage, Kategorie, Outcomes, `ConditionId`↔`TokenId`) ändern sich nie — einmal +ziehen, lang halten; nur Volumen, Liquidität und Auflösung periodisch aktualisieren. +Teilweise erledigt: geschlossene Märkte werden nur noch einmal täglich synchronisiert. + +### 5.3 🟡 Egress-Kanäle: Betriebsreste + +Die Mechanik steht (Round-Robin, Cooldown nach drei Fehlschlägen, Limiter je Kanal). Was +im Ingest-Plan noch danebensteht: + +- [ ] Health-Statistik je Kanal — Erfolgsquote, 429-Rate, Latenz. Heute gibt es nur einen + Fehlerzähler: tote Kanäle werden pausiert, aber nicht ausgewertet +- [ ] Bei eigenen IPs prüfen, ob es echte getrennte Egress-IPs sind (multi-homed) und nicht + NAT hinter derselben öffentlichen Adresse — sonst bringt die Aufteilung nichts +- [ ] Regressionsschutz: die MySQL-Verbindung darf **nie** über den Egress-Pool oder einen + Proxy laufen +- [ ] Grob unplausible Provider-Antworten abfangen (z. B. Preis außerhalb 0–1) + +### 5.4 ❌ Header- und User-Agent-Rotation + +Im Ingest-Plan bewusst als „standardmäßig AUS" eingeordnet. Rotation zur Verschleierung +gegenüber Polymarket ist gegen deren Interesse gerichtet und riskiert die Sperrung genau des +Zugangs, auf dem das Projekt aufsetzt. Der ehrliche Weg ist, die Nachfrage zu senken (5.1, +5.2) und legitime Kanäle zu erhöhen (bereits umgesetzt). + +--- + +## Stufe 6 — Monetarisierung vorbereiten + +Nur relevant, falls aus dem internen Werkzeug ein Produkt werden soll. **Bislang eine Idee, +keine Entscheidung.** Die Vorarbeit (Read/Control-Trennung, `/api/capabilities`, +CORS-Whitelist) ist im Zuge der Portierung ohnehin schon passiert. + +> Details: [`docs/archiv/PLAN-Architektur-WebUI-Backend.md`](docs/archiv/PLAN-Architektur-WebUI-Backend.md). + +### 6.1 💡 Datenbank für eine öffentliche Schicht vorbereiten + +- **Read-Endpunkte auf versteckte Writes und Provider-Aufrufe prüfen.** Der Read/Control-Split + steht, aber ob jeder einzelne Read-Endpunkt frei von Schreibzugriffen ist, wurde nie + systematisch geprüft — besonders der Deep-Dive-Pfad. Vor einem öffentlichen Host + nachzuholen, sonst wird die read-only Verbindung im Betrieb Fehler werfen +- Read-only DB-Benutzer auf der Analyse-DB (nur `SELECT`) — 👤 der Nutzer führt die Grants aus +- Schema-Kopplung dokumentieren: liest eine öffentliche Schicht direkt mit, wird das + DB-Schema zur **Vertragsfläche**. Views als Puffer erwägen, damit interne Umbauten die + öffentliche Sicht nicht brechen. + +Billig und jederzeit vorziehbar, auch wenn 6.2 nie kommt. + +### 6.2 💡 Öffentlicher Host + +Eigenes Projekt `Predictalytics.PublicApi`: registriert nur die Read-Endpunkte, dazu +Konten/Authentifizierung in einer **getrennten** User-DB, Rate-Limits und Kontingente pro +Konto, Mandantensicht (Kunde sieht seine Watchlist, nicht das ganze Universum), liefert +dasselbe SPA aus. + +**Nicht-Ziele, die dabei gelten** — ❌ kein Zugriff der öffentlichen Schicht auf Core-Prozess, +lokale API oder Steuerendpunkte; ❌ kein Schreibzugriff auf die Analyse-DB; ❌ keine +Kunden-Authentifizierung nachträglich in die lokale Vollschicht bauen; ❌ Read-Endpunkte +machen keine Provider-Aufrufe und keine Writes. + +--- + +## Anhang — bewusst nicht auf der Roadmap + +Geprüft und mit Absicht ausgelassen. Nicht versehentlich wieder aufnehmen: + +| Was | Warum | +|---|---| +| `AzuroProvider` löschen | Platzhalter mit `IsImplemented => false`, wird sauber übersprungen. Kostet nichts, markiert den Ausbaupfad. | +| `Providers/Limitless/` löschen | Voll implementierter zweiter Marktplatz, `IsImplemented => true`. Wird nur nicht bespielt — Löschen wäre Funktionsverlust. | +| `wwwroot/landing.html`, `docs.html` | Marketing-Seiten mit Preis-Abschnitt, vom Dashboard nicht verlinkt. Gehören zu Stufe 6. | +| Kaltarchiv außerhalb der DB | Verworfen: Rohdaten sind über die Polymarket-API jederzeit nachladbar, ein Archiv wäre reine Zusatzlast. | +| EF Core auf 10 heben | Blockiert: Pomelo hat keinen EF-10-Provider und pinnt Relational hart auf `[9.0.0, 9.0.999]`. Vor einem Versuch prüfen, ob Pomelo nachgezogen hat. | diff --git a/STATUS.md b/STATUS.md index 22dc438..2497cb7 100644 --- a/STATUS.md +++ b/STATUS.md @@ -4,8 +4,9 @@ > gegen den Code geprüft, nicht aus den Plandokumenten übernommen — die waren zu diesem > Zeitpunkt durchweg veraltet. > -> Dieses Dokument ist die Einstiegsseite. Die Detailpläne bleiben bestehen und tragen -> jetzt jeweils oben einen eigenen Statusblock. +> Dieses Dokument beschreibt den **Ist-Stand**. Was ansteht, steht in +> [`ROADMAP.md`](ROADMAP.md); die abgelösten Plandokumente liegen unter +> [`docs/archiv/`](docs/archiv/). --- @@ -46,27 +47,28 @@ dem Predictalytics-Checkout liegen, sonst bricht der Build mit einer erklärende ## 2. Was fertig ist -Alles Folgende ist im Code belegt — die Belegstellen stehen in den jeweiligen Plandokumenten. +Alles Folgende ist im Code belegt; die Belegstellen stehen in den archivierten Plandokumenten +unter [`docs/archiv/`](docs/archiv/). -* **Kernanalytik** (`UMSETZUNGSPLAN.md`, Phase 1): positionsbasierte PnL-Engine nach +* **Kernanalytik** ([`archiv/UMSETZUNGSPLAN.md`](docs/archiv/UMSETZUNGSPLAN.md), Phase 1): positionsbasierte PnL-Engine nach Average-Cost mit sauberer Behandlung jeder `TradeSide`, marktbasierte Win-Rate, Deep-Dive-Kennzahlen aus echter Preishistorie, Copytrading-Score aus gemessenem Alpha-Verfall gegen das Markt-Tape. * **Robustheit** (Phase 2): EF-Migrationen statt handgeschriebener `ALTER TABLE`, entkoppelte Scoring-Pipeline, Log-Retention, Testprojekt. Alle fünf Bugs aus dem Review-Pass vom 2026-07-01 sind behoben. -* **Speicher-Haushalt** (`FIXPLAN-G-Speicher.md`): budgetabhängiges Retention-Fenster, +* **Speicher-Haushalt** ([`archiv/FIXPLAN-G-Speicher.md`](docs/archiv/FIXPLAN-G-Speicher.md)): budgetabhängiges Retention-Fenster, Burst-Kompaktierung, Per-Kategorie-Edge. -* **Kategorie-Erkennung** (`FIXPLAN-TODO.md`, Teil F): kanonische Tag-Tabelle, +* **Kategorie-Erkennung** ([`archiv/FIXPLAN-TODO.md`](docs/archiv/FIXPLAN-TODO.md), Teil F): kanonische Tag-Tabelle, Rausch-Filter, Tag-Nachladen für On-Demand-Märkte, Offline-Backfill beim Start. * **HF-Trader-Tiering** (Teil D): `IngestMode` mit Hysterese, wöchentliche Biopsie, Aggregations-Buckets. -* **Web-UI** (`FIXPLAN-UI-Ranglisten.md`): Showcase-Sektionen, serverseitige Sortierung +* **Web-UI** ([`archiv/FIXPLAN-UI-Ranglisten.md`](docs/archiv/FIXPLAN-UI-Ranglisten.md)): Showcase-Sektionen, serverseitige Sortierung und Filterung. * **Copytrading-Analytik**: Co-Movement samt Clusterung, Strategie-Drift, Edge-Freshness, Copy-Portfolio, Insider-Feed — alle fünf Vorschläge der Roadmap sind umgesetzt und in diesem Zweig enthalten. -* **Portierung** (`docs/PLAN-Linux-Portierung.md`, Phasen 0–4): .NET 10, extrahierter +* **Portierung** ([`archiv/PLAN-Linux-Portierung.md`](docs/archiv/PLAN-Linux-Portierung.md), Phasen 0–4): .NET 10, extrahierter Hosting-Kern, Avalonia-Shell, abgesicherte Steuerendpunkte, plattformfeste Kultur- und Log-Pfade. Der WinForms-Host ist entfernt. * **Betriebsanbindung**: Lizenz, Watchdog, UpdateService, Fehler-Stream und Bugtracker @@ -76,47 +78,17 @@ Alles Folgende ist im Code belegt — die Belegstellen stehen in den jeweiligen ## 3. Was offen ist -Nach Dringlichkeit: +Die offenen Punkte stehen vollständig und nach Reihenfolge sortiert in +**[`ROADMAP.md`](ROADMAP.md)** — hier nur die drei, die den Blick aufs Ganze bestimmen: -### 3.1 ⚠️ OpenRouter-API-Key liegt im Klartext im Repository - -`src/Predictalytics.Api/appsettings.json` enthält einen **echten** OpenRouter-Schlüssel, -versioniert seit Commit `7045002` und damit auf dem Gitea-Server. - -**Zu tun:** Den Schlüssel bei OpenRouter zurückziehen und neu ausstellen. Ihn nur aus der -Datei zu entfernen genügt nicht — der Git-Verlauf behält ihn. Anschließend über die -Umgebungsvariable `OpenRouter__ApiKey` setzen; eine Codeänderung braucht es dafür nicht, -`WebApplication.CreateBuilder` liest Umgebungsvariablen bereits mit Vorrang. - -Der Schlüssel wurde bewusst **nicht** eigenmächtig entfernt: ohne Rotation bringt das -keinen Sicherheitsgewinn, würde aber die KI-Strategieanalyse stillschweigend abschalten. - -### 3.2 Die Portierung ist unter Linux nie gelaufen - -Phase 5 (Deployment, systemd/Container, Reverse Proxy, CI) und Phase 6 (Verifikation) des -Portierungsplans sind unangetastet. Der Code ist plattformneutral gebaut, aber **auf keinem -Linux-System je ausgeführt worden**. Die Ende-zu-Ende-Prüfung vom 2026-08-08 lief unter -Windows. Solange das aussteht, ist „läuft unter Linux" eine Annahme, kein Befund. - -### 3.3 A4 — Strategie-Klassifikation - -Der einzige größere unerledigte Block aus Phase 1. `StrategyType` entsteht weiterhin aus -zwei Signalen — `avgSize > 10000` ergibt `Whale`, `hedgingRate > 30` ergibt `Hedger`, sonst -`Bot`/`Unknown` — weshalb der Großteil der Trader strukturell auf `Unknown` landet. Die -Bausteine für eine bessere Einstufung liegen fertig herum (`StrategyMetricsCalculator`, -`TraderTraitCalculator`, `FingerprintSnapshotService`), sie speisen die Klassifikation nur -nicht. - -Die Logik steht außerdem **doppelt** da: `AnalyticsService.cs:418` und -`ScoringService.cs:89`. Beim Anfassen zusammenführen. - -### 3.4 Kleinere Reste - -* `FIXPLAN-UI-Ranglisten.md`: Pfeilrichtung ▲/▼ in der aktiven Sortierspalte (war als - „Optional" markiert). -* `FIXPLAN-TODO.md` F5/F6: gezielter Nachlade-Schritt für fehlende Event-Tags, zwei Tests. -* `UMSETZUNGSPLAN.md` C3 und D1/D2: DB-Arbeiten auf SQL-Ebene und die Entscheidung über - einen Datenbank-Neustart — beides liegt beim Nutzer. +1. **Ein echter OpenRouter-API-Key liegt im Klartext im Repository** + (`src/Predictalytics.Api/appsettings.json`, versioniert seit `7045002`). Er muss beim + Anbieter zurückgezogen und neu ausgestellt werden — ihn nur zu löschen hilft nicht, die + Git-Historie behält ihn. Roadmap 0.1. +2. **Die Portierung ist auf keinem Linux-System je gelaufen.** Der Code ist plattformneutral + gebaut, die Ende-zu-Ende-Prüfung vom 2026-08-08 lief aber unter Windows. Roadmap Stufe 2. +3. **Die Strategie-Klassifikation ist faktisch wirkungslos** — zwei Signale, der Großteil + der Trader landet auf `Unknown`, und die Logik steht doppelt da. Roadmap 3.1. --- @@ -142,16 +114,17 @@ Avalonia-Shell noch die Deploymentcenter-Anbindung. ## 5. Bewusst stehengelassen Beim Aufräumen geprüft und **nicht** entfernt, weil Löschen hier Funktionsverlust wäre und -nicht Entrümpelung: +nicht Entrümpelung. Dieselbe Liste steht im Anhang der [`ROADMAP.md`](ROADMAP.md), damit sie +nicht versehentlich als Aufgabe wieder auftaucht: | Was | Warum es bleibt | |---|---| | `Providers/Azuro/AzuroProvider.cs` | Platzhalter mit `IsImplemented => false`. Die Worker überspringen ihn sauber; er kostet nichts und markiert den Ausbaupfad. | | `Providers/Limitless/` (3 Dateien) | **Voll implementierter** zweiter Marktplatz mit `IsImplemented => true`. Wird produktiv nur nicht bespielt. | -| `Services/CopytradingBacktestHarness.cs` (123 Z.) | Fertig implementierter Latenz-Sweep, per DI registriert, aber **von niemandem aufgerufen**. Entweder anschließen oder bewusst verwerfen — beides eine fachliche Entscheidung. | +| `Services/CopytradingBacktestHarness.cs` (123 Z.) | Fertig implementierter Latenz-Sweep, per DI registriert, aber **von niemandem aufgerufen**. Entscheidung steht aus, siehe Roadmap 3.2. | | `wwwroot/landing.html`, `wwwroot/docs.html` | Marketing-Seiten mit Preis-Abschnitt, vom Dashboard aus nicht verlinkt. Passen zur später angedachten Monetarisierung. | | 8 Nullable-Warnungen | `TradeRepository.cs:94-102`, `PolymarketProvider.cs:357`, `TraderAnalyticsWorker.cs:152`. Bestehen seit Längerem; sie zu beheben ist Logikarbeit, kein Aufräumen. | -| 65 Migrationsdateien | Die Schemahistorie. Zusammenfassen ginge, ist aber riskant und sollte eine bewusste Entscheidung sein. | +| 65 Migrationsdateien | Die Schemahistorie. Zusammenfassen ginge, ist aber riskant — siehe Roadmap 4.4. | Ein Nebenbefund ohne Handlungszwang: der Kategorie-Backfill beim Start (`PredictalyticsHost.cs:116`) lädt **alle** Märkte samt Event in den Speicher. Bei diff --git a/docs/ANALYSE-Linux-Portierung.md b/docs/archiv/ANALYSE-Linux-Portierung.md similarity index 100% rename from docs/ANALYSE-Linux-Portierung.md rename to docs/archiv/ANALYSE-Linux-Portierung.md diff --git a/FIXPLAN-DONE.md b/docs/archiv/FIXPLAN-DONE.md similarity index 100% rename from FIXPLAN-DONE.md rename to docs/archiv/FIXPLAN-DONE.md diff --git a/FIXPLAN-G-Speicher.md b/docs/archiv/FIXPLAN-G-Speicher.md similarity index 100% rename from FIXPLAN-G-Speicher.md rename to docs/archiv/FIXPLAN-G-Speicher.md diff --git a/FIXPLAN-TODO.md b/docs/archiv/FIXPLAN-TODO.md similarity index 100% rename from FIXPLAN-TODO.md rename to docs/archiv/FIXPLAN-TODO.md diff --git a/FIXPLAN-UI-Ranglisten.md b/docs/archiv/FIXPLAN-UI-Ranglisten.md similarity index 100% rename from FIXPLAN-UI-Ranglisten.md rename to docs/archiv/FIXPLAN-UI-Ranglisten.md diff --git a/docs/PLAN-Architektur-WebUI-Backend.md b/docs/archiv/PLAN-Architektur-WebUI-Backend.md similarity index 85% rename from docs/PLAN-Architektur-WebUI-Backend.md rename to docs/archiv/PLAN-Architektur-WebUI-Backend.md index 65e82b7..f74b08e 100644 --- a/docs/PLAN-Architektur-WebUI-Backend.md +++ b/docs/archiv/PLAN-Architektur-WebUI-Backend.md @@ -1,5 +1,27 @@ # Plan: Drei-Schichten-Architektur (Core / Lokal / Public) +> **Archiviert am 2026-08-23** — offene Punkte siehe [`ROADMAP.md`](../../ROADMAP.md), +> Stufe 6. Dieses Dokument wird nicht mehr gepflegt. +> +> **Phase 0 bis 2 sind erledigt**, überwiegend als Nebenprodukt der Linux-Portierung, und +> hier nachträglich abgehakt: +> * Read/Control-Split (`ApiConfiguration.MapPredictalyticsReadEndpoints()` bzw. +> `MapPredictalyticsControlEndpoints()`), `/api/dev/*` und die Job-Trigger liegen auf der +> Control-Seite. +> * `GET /api/capabilities` existiert und meldet den tatsächlichen Zustand; die WebUI +> koppelt ihre Admin-Aktionen daran (`app.js:880,1132`). +> * `API_BASE` ist im SPA konfigurierbar (`app.js:2`), Trait-Chips sind da. +> * CORS läuft über eine Origin-Whitelist, die Bind-Adresse ist explizit +> (`PredictalyticsHost.cs:177,198-201`) — kein `0.0.0.0`. +> +> **Nicht abgehakt** ist der Deep-Dive-Audit auf versteckte Writes in Read-Endpunkten +> (Phase 0, dritter Punkt): die Trennung steht, ob jeder Read-Endpunkt einzeln daraufhin +> geprüft wurde, lässt sich im Nachhinein nicht belegen. Vor einem öffentlichen Host +> nachholen. +> +> **Phase 3 und 4 sind offen** und bilden Roadmap-Stufe 6 — dort als Konzept markiert, +> weil über eine Monetarisierung nicht entschieden ist. + > Stand: 2026-07-12 · Status: **Architektur bestätigt** (Nutzer-Entscheidung), Implementierung > schrittweise. Kein Code in diesem Schritt. > Kontext: Neues WebUI-Design (mit Claude Design vorbereitet) wird nach der Schicht-Trennung @@ -88,24 +110,24 @@ und von beiden Hosts wiederverwendet — kein Duplizieren: ## 3. Implementierungsplan (schrittweise) ### Phase 0 — Endpunkte klassifizieren & Read/Control trennen (Fundament, klein) -- [ ] Jeden Endpunkt in `docs/API.md` als **Read** (public-tauglich) oder **Control** (nur lokal) +- [x] Jeden Endpunkt in `docs/API.md` als **Read** (public-tauglich) oder **Control** (nur lokal) markieren. Kandidaten Control: alle `POST/PUT/DELETE`, `/api/dev/*`, Job-Trigger. - [ ] Auf **versteckte Writes/Provider-Calls** in Read-Endpunkten prüfen (Deep-Dive!) und bereinigen oder als Control einstufen. -- [ ] `MapPredictalyticsEndpoints()` in `MapReadEndpoints()` + `MapControlEndpoints()` splitten +- [x] `MapPredictalyticsEndpoints()` in `MapReadEndpoints()` + `MapControlEndpoints()` splitten (rein struktureller Refactor, Verhalten unverändert; bestehende Tests bleiben grün). -- [ ] `GET /api/capabilities` einführen. +- [x] `GET /api/capabilities` einführen. ### Phase 1 — WebUI als sauberes, gemeinsames SPA (parallel zum neuen Design) -- [ ] Neues Design als **ein statisches Artefakt** bauen, reiner API-Client, `API_BASE` +- [x] Neues Design als **ein statisches Artefakt** bauen, reiner API-Client, `API_BASE` konfigurierbar (heute `''` = gleiche Origin bleibt lokal gültig). -- [ ] Admin-Aktionen an `capabilities.canControl` koppeln. -- [ ] Trait-Chips/-Filter im Design vorsehen — **erscheinen erst, wenn Backend-D2 steht** +- [x] Admin-Aktionen an `capabilities.canControl` koppeln. +- [x] Trait-Chips/-Filter im Design vorsehen — **erscheinen erst, wenn Backend-D2 steht** (kein UI-Bug, das Backend liefert Traits noch nicht). ### Phase 2 — Lokale Grenze härten (klein, jetzt schon sinnvoll) -- [ ] CORS von `AllowAnyOrigin` auf konfigurierte Origin-Whitelist umstellen. -- [ ] Bind-Adresse explizit (localhost/LAN), nie `0.0.0.0` ohne Firewall; Konfig-Kommentar +- [x] CORS von `AllowAnyOrigin` auf konfigurierte Origin-Whitelist umstellen. +- [x] Bind-Adresse explizit (localhost/LAN), nie `0.0.0.0` ohne Firewall; Konfig-Kommentar „NIEMALS ins Internet — das ist die interne Schicht". ### Phase 3 — DB für Public vorbereiten (mittel, kann früh passieren) diff --git a/docs/PLAN-DatenIngest-Skalierung.md b/docs/archiv/PLAN-DatenIngest-Skalierung.md similarity index 79% rename from docs/PLAN-DatenIngest-Skalierung.md rename to docs/archiv/PLAN-DatenIngest-Skalierung.md index 9625f10..eb2386a 100644 --- a/docs/PLAN-DatenIngest-Skalierung.md +++ b/docs/archiv/PLAN-DatenIngest-Skalierung.md @@ -1,5 +1,26 @@ # Plan: Daten-Ingest skalieren (Rate-Limits, Egress-Kanäle, Blockchain) +> **Archiviert am 2026-08-23** — offene Punkte siehe [`ROADMAP.md`](../../ROADMAP.md), +> Stufe 5. Dieses Dokument wird nicht mehr gepflegt. +> +> **Erledigt und hier nachträglich abgehakt:** +> * **1c Ingest-Tiering** — `IngestMode` mit `SnapshotOnly`/`Aggregated`, siehe +> [`FIXPLAN-TODO.md`](FIXPLAN-TODO.md) Teil D. +> * **1d Redundanz** — `IMemoryCache` im `PolymarketApiClient`. +> * **2a/2b Egress-Kanäle** — `Infrastructure/Services/EgressPoolService.cs` und +> `EgressPoolHandler.cs`: Round-Robin über die aktiven Kanäle, Umschalten rein per +> Konfiguration, Cooldown nach drei Fehlschlägen in Folge (`:152`), sodass ein 429 nur +> den betroffenen Kanal trifft. Der Limiter-Schlüssel enthält den Kanal +> (`RateLimiterService.cs:36`). Tests: `EgressPoolTests.cs`. +> +> **Teilweise:** Health-Statistik je Kanal (Erfolgsquote, 429-Rate, Latenz) fehlt — es gibt +> nur Fehlerzähler und Cooldown. Tote Kanäle werden also pausiert, aber nicht ausgewertet. +> +> **Offen:** 1a (Blockchain/Subgraph als Bulk-Quelle) und 1b (Markt-Stammdaten-Cache) sind +> Roadmap 5.1 und 5.2. **Abschnitt 3a (Header-Rotation) ist bewusst verworfen** — siehe +> Roadmap 5.3: Verschleierung gegenüber Polymarket riskiert genau den Zugang, auf dem das +> Projekt aufsetzt. + > Stand: 2026-07-12 · Status: **Richtung bestätigt** (Nutzer: beide Egress-Wege umschaltbar > umsetzen, sofern Aufwand vertretbar). Kein Code in diesem Schritt. > Problem: Polymarket-Rate-Limits bremsen den Datenimport teils extrem aus. @@ -32,11 +53,11 @@ REST-Verbraucher) ist `/activity` die falsche Quelle. nur Volume/Liquidity/Auflösung periodisch aktualisieren. Geschlossene Märkte nie voll re-syncen. ### 1c. Ingest-Tiering (FIXPLAN D3) -- [ ] Ultra-HF-Trader im `SnapshotOnly`-Modus erzeugen null Trade-Calls (PnL aus `/positions` + +- [x] Ultra-HF-Trader im `SnapshotOnly`-Modus erzeugen null Trade-Calls (PnL aus `/positions` + Leaderboard, wöchentliche Biopsie). Entfernt genau die Wallets, die das Budget auffressen. ### 1d. Redundanz vermeiden -- [ ] Worker-übergreifender Kurzzeit-Cache „gerade geholt", damit nicht mehrere Worker denselben +- [x] Worker-übergreifender Kurzzeit-Cache „gerade geholt", damit nicht mehrere Worker denselben Markt/Trader kurz hintereinander abrufen. --- @@ -59,18 +80,18 @@ Ein Kanal ist genau eine Ausgangsroute, per Config als einer von zwei Typen defi ] } ``` -- [ ] Pro Kanal **ein** `SocketsHttpHandler`: +- [x] Pro Kanal **ein** `SocketsHttpHandler`: - `SourceIp` → `ConnectCallback`, Socket vor Connect an die lokale IP binden (`socket.Bind(new IPEndPoint(ip, 0))`). - `Proxy` → `handler.Proxy = new WebProxy(url); handler.UseProxy = true;`. -- [ ] `IEgressPool` verteilt Requests round-robin/least-loaded über die aktiven Kanäle. +- [x] `IEgressPool` verteilt Requests round-robin/least-loaded über die aktiven Kanäle. Leere/❑ Kanalliste = heutiges Verhalten (ein Default-Ausgang). -- [ ] Umschalten „nur eigene IPs" ↔ „nur Proxys" ↔ „Mix" = Config ändern, kein Deploy-Umbau. +- [x] Umschalten „nur eigene IPs" ↔ „nur Proxys" ↔ „Mix" = Config ändern, kein Deploy-Umbau. ### 2b. Rate-Limiter **pro Kanal** (Generalisierung des globalen Limiters) -- [ ] Limiter-Schlüssel wird `{platform}-{endpointGroup}-{channelId}`. Jeder Kanal hält sein +- [x] Limiter-Schlüssel wird `{platform}-{endpointGroup}-{channelId}`. Jeder Kanal hält sein eigenes Budget → N Kanäle ≈ N× Durchsatz, jeder Kanal bleibt unter dem Per-Route-Limit. -- [ ] 429 sperrt **nur den betroffenen Kanal** kurz, nicht alle. +- [x] 429 sperrt **nur den betroffenen Kanal** kurz, nicht alle. ### 2c. Betrieb - [ ] Bei eigenen IPs prüfen: sind es echte getrennte Egress-IPs (multi-homed), nicht NAT hinter einer. @@ -123,10 +144,10 @@ Auf Wunsch integriert, bewusst opt-in: (welcher Subgraph deckt Fills sauber ab?), dann als `IHistoricalTradeSource`. ## 5. Tests / Abnahme -- [ ] Per-Kanal-Limiter: unabhängige Budgets (kein globales Blocken); 429 sperrt nur einen Kanal. -- [ ] Egress-Binding: Smoke gegen einen Dienst, der die Quell-IP zurückgibt → Round-Robin nutzt +- [x] Per-Kanal-Limiter: unabhängige Budgets (kein globales Blocken); 429 sperrt nur einen Kanal. +- [x] Egress-Binding: Smoke gegen einen Dienst, der die Quell-IP zurückgibt → Round-Robin nutzt wirklich verschiedene IPs; Proxy-Kanal geht über den Proxy. -- [ ] Kanal-Health: toter Proxy wird automatisch pausiert, Pool weicht aus. +- [x] Kanal-Health: toter Proxy wird automatisch pausiert, Pool weicht aus. - [ ] Blockchain-Quelle: Stichproben-Abgleich Bulk-Historie ↔ REST-`/activity` eines Wallets, bevor sie produktiv wird. - [ ] **Header-Rotation:** bei `Enabled=false` wird genau ein konsistenter Standard-Satz gesendet diff --git a/docs/PLAN-Linux-Portierung.md b/docs/archiv/PLAN-Linux-Portierung.md similarity index 100% rename from docs/PLAN-Linux-Portierung.md rename to docs/archiv/PLAN-Linux-Portierung.md diff --git a/docs/archiv/README.md b/docs/archiv/README.md new file mode 100644 index 0000000..ee40105 --- /dev/null +++ b/docs/archiv/README.md @@ -0,0 +1,25 @@ +# Archiv — abgelöste Plandokumente + +Diese Dokumente wurden am **2026-08-23** hierher verschoben. Sie werden **nicht mehr +gepflegt**. Was von ihnen noch offen war, steht gebündelt in [`ROADMAP.md`](../../ROADMAP.md); +der Ist-Stand des Projekts in [`STATUS.md`](../../STATUS.md). + +**Wozu sie trotzdem da sind:** Die Roadmap nennt jeden offenen Punkt knapp. Die Herleitung — +warum etwas so entschieden wurde, welche Messungen dahinterstehen, welche Alternativen +verworfen wurden — steht hier. Wer einen Roadmap-Punkt umsetzt, findet hier den Kontext. + +**Vorsicht bei der Lektüre:** Der Text unterhalb des jeweiligen Statusblocks beschreibt den +Stand seiner Entstehungszeit. Test-Baselines, Dateipfade und „so ist es heute"-Aussagen sind +teils überholt. Immer gegen den Code prüfen, nie direkt übernehmen. + +| Dokument | Stand | Was drinsteht | +|---|---|---| +| [`UMSETZUNGSPLAN.md`](UMSETZUNGSPLAN.md) | 2026-07-01, abgehakt 2026-08-23 | Phase 1 (Kernanalytik: PnL-Engine, Win-Rate, Deep-Dive, Copytrading-Score) und Phase 2 (Migrationen, Scoring-Entkopplung, Bugliste, Tests) sowie das Speicherplatz-Konzept. Bis auf A4 und B2 umgesetzt. | +| [`FIXPLAN-DONE.md`](FIXPLAN-DONE.md) | 2026-07-09 | Vorgängerfassung des Fix-Plans, vollständig abgearbeitet. Reine Historie. | +| [`FIXPLAN-TODO.md`](FIXPLAN-TODO.md) | 2026-07-09/11/13 | Teil D (Merkmals-Tags, HF-Trader-Tiering mit `IngestMode`) und Teil F (Kategorie-Erkennung: kanonische Tags, Rausch-Filter, Backfill). Umgesetzt bis auf F5 und zwei Tests. | +| [`FIXPLAN-G-Speicher.md`](FIXPLAN-G-Speicher.md) | 2026-07-17 | Speicher-Governor, Retention für aggregierte Trader, `UsdcSize`/`OutcomeIndex`, Per-Kategorie-Edge. Vollständig umgesetzt; der Anhang mit den einmaligen SQL-Optimierungen ist offen. | +| [`FIXPLAN-UI-Ranglisten.md`](FIXPLAN-UI-Ranglisten.md) | 2026-08-03 | Web-UI: Showcase-Sektionen, serverseitige Sortierung und Filterung. Vollständig umgesetzt bis auf die Sortierpfeile. | +| [`PLAN-Linux-Portierung.md`](PLAN-Linux-Portierung.md) | 2026-08-06 bis -08 | Der Portierungsplan mit allen Entscheidungen und Abnahmevermerken. **Die ausführlichste Quelle für Roadmap-Stufe 2** — Abschnitt 7 (Deployment) und 8 (Verifikationsmatrix) sind dort detaillierter als in der Roadmap. | +| [`ANALYSE-Linux-Portierung.md`](ANALYSE-Linux-Portierung.md) | 2026-08-06 | Die Bestandsaufnahme, die dem Portierungsplan vorausging: was am WinForms-Host hing, welche Pakete windows-spezifisch waren. Historisch. | +| [`PLAN-Architektur-WebUI-Backend.md`](PLAN-Architektur-WebUI-Backend.md) | 2026-07 | Drei-Schichten-Konzept Core/Lokal/Public als Vorbereitung einer Monetarisierung. Phase 0–2 sind im Zuge der Portierung erledigt; Phase 3–4 sind Roadmap-Stufe 6. | +| [`PLAN-DatenIngest-Skalierung.md`](PLAN-DatenIngest-Skalierung.md) | 2026-07 | Rate-Limits, Egress-Kanäle, Blockchain als Bulk-Quelle. Egress-Kanäle und Ingest-Tiering sind umgesetzt; der Rest ist Roadmap-Stufe 5. | diff --git a/UMSETZUNGSPLAN.md b/docs/archiv/UMSETZUNGSPLAN.md similarity index 98% rename from UMSETZUNGSPLAN.md rename to docs/archiv/UMSETZUNGSPLAN.md index 7d4d325..5100a80 100644 --- a/UMSETZUNGSPLAN.md +++ b/docs/archiv/UMSETZUNGSPLAN.md @@ -138,7 +138,7 @@ Beim Review ist aufgefallen, dass `TraderAnalyticsWorker.CalculatePnL` **jeden T - [x] Eigenes Intervall für volle Neuberechnung/Ranking (z.B. alle 15 Min) getrennt vom reinen Trade-Polling (60s) ### B6. Weitere gefundene Bugs/Performance-Probleme (Review-Pass 2026-07-01) -- [x] **`MarketSyncWorker` re-synct alle 30 Minuten ALLE Märkte, inkl. `includeClosed=true`, von Offset 0** ([MarketSyncWorker.cs](src/Predictalytics.Worker/Services/MarketSyncWorker.cs)) — holt damit bei jedem Zyklus jeden jemals geschlossenen Polymarket-Markt erneut komplett durch. Wächst unbegrenzt mit der Zeit, echtes Risiko für Rate-Limiting/Sperrung. Sollte auf: aktive Märkte häufig, geschlossene Märkte selten/inkrementell (z.B. nur kürzlich geschlossene, nicht der komplette Bestand) umgestellt werden +- [x] **`MarketSyncWorker` re-synct alle 30 Minuten ALLE Märkte, inkl. `includeClosed=true`, von Offset 0** ([MarketSyncWorker.cs](../../src/Predictalytics.Worker/Services/MarketSyncWorker.cs)) — holt damit bei jedem Zyklus jeden jemals geschlossenen Polymarket-Markt erneut komplett durch. Wächst unbegrenzt mit der Zeit, echtes Risiko für Rate-Limiting/Sperrung. Sollte auf: aktive Märkte häufig, geschlossene Märkte selten/inkrementell (z.B. nur kürzlich geschlossene, nicht der komplette Bestand) umgestellt werden - [x] **Watchlisted Trader nicht von Auto-Löschung ausgeschlossen** (`TraderRepository.GetTradersForCleanupAsync` / `TraderCleanupWorker`) — da `Trades`/`TraderScore`/`WatchlistEntries` per Cascade am Trader hängen, könnte ein manuell beobachteter Trader nach 1 Jahr Inaktivität (oder bei kurzzeitigem API-Fehler) unbemerkt komplett gelöscht werden. Watchlist-Einträge sollten von der Cleanup-Query ausgenommen werden - [x] **`TradeRepository.GetKnownPlatformTradeIdsAsync` lädt die komplette Trade-ID-Historie eines Traders ins RAM**, nur um eine kleine neu geholte Charge (~100-1000 Trades) zu deduplizieren — aufgerufen alle 60s (`PollingWorker`) bzw. alle 12h (`TradeHistoryWorker`) pro Trader. Wird mit wachsender Trade-Zahl immer teurer. Fix: nur `WHERE PlatformTradeId IN ()` abfragen statt der gesamten Historie - [x] **Race Condition in `MarketRepository.AddOrUpdateAsync`** (im Gegensatz zu `AddOrUpdateRangeAsync` ohne Locking) — `TradeHistoryWorker` verarbeitet bis zu 5 Trader parallel (`Parallel.ForEachAsync`); referenzieren zwei gleichzeitig denselben noch unbekannten Markt, prüfen beide unabhängig "existiert nicht" und einer crasht beim `Add` mit Unique-Constraint-Verletzung (wird geloggt, Trader-Sync für den Zyklus bricht ab, nächster Zyklus heilt es meist). Fix: gleiches Locking-Muster wie `AddOrUpdateRangeAsync` verwenden, oder Insert-Konflikt sauber abfangen/retry diff --git a/src/Predictalytics.Infrastructure/DependencyInjection.cs b/src/Predictalytics.Infrastructure/DependencyInjection.cs index 79b0077..daa9a7e 100644 --- a/src/Predictalytics.Infrastructure/DependencyInjection.cs +++ b/src/Predictalytics.Infrastructure/DependencyInjection.cs @@ -121,7 +121,7 @@ public static class DependencyInjection /// /// Applies pending EF Core migrations and seeds default platform rows. /// Requires the target database to already be "stamped" with the InitialBaseline - /// migration in __EFMigrationsHistory (see UMSETZUNGSPLAN.md, section B1) if it was + /// migration in __EFMigrationsHistory (see docs/archiv/UMSETZUNGSPLAN.md, B1) if it was /// previously created via the old EnsureCreated + manual ALTER approach. /// public static async Task EnsureDatabaseAsync(IServiceProvider services, bool dbDebug = false)