Added B6 to UMSETZUNGSPLAN.md: unbounded MarketSyncWorker re-sync of all closed markets, watchlisted traders not exempt from auto-cleanup deletion, full trade-history load in GetKnownPlatformTradeIdsAsync, a race condition in MarketRepository.AddOrUpdateAsync under concurrent access, and an early-break assumption in TradeHistoryWorker that isn't applied consistently. No functional code changed yet — these are earmarked for Phase 2 (B3-adjacent).
191 lines
22 KiB
Markdown
191 lines
22 KiB
Markdown
# Predictalytics – Umsetzungsplan (Phase 1 & 2 + Storage-Optimierung)
|
||
|
||
> **Wie wir das nutzen:** Wir arbeiten diese Liste Stück für Stück ab. Erledigte Punkte werden von `[ ]` auf `[x]` gesetzt. Reihenfolge ist absichtlich so gewählt, dass spätere Schritte auf früheren aufbauen — nicht einfach querbeet abhaken, siehe Abschnitt "Empfohlene Reihenfolge" ganz unten für die Kurzfassung.
|
||
>
|
||
> Phase 3 (Monetarisierung: Auth, Multi-Tenant, Billing) ist bewusst **nicht** Teil dieses Plans.
|
||
|
||
---
|
||
|
||
## 0. Vorab-Bugfund (betrifft Phase 1 direkt)
|
||
|
||
Beim Review ist aufgefallen, dass `TraderAnalyticsWorker.CalculatePnL` **jeden Trade, der kein `Buy` ist, wie ein `Sell` behandelt** (`else pnl += t.Amount`). Das ist falsch für `Split`, `Merge`, `Redeem`, `AddLiquidity`, `RemoveLiquidity` — das sind keine gewöhnlichen Verkäufe. Dieser Fix gehört zwingend in die neue PnL-Engine (siehe A1/A6), nicht als Extra-Task.
|
||
|
||
---
|
||
|
||
## Phase 1 – Kernanalytik korrigieren
|
||
|
||
### A1. Positionsbasierte PnL-Engine
|
||
- [ ] Entscheidung dokumentieren: **Average-Cost-Methode** statt FIFO (einfacher, und – wichtig – kompatibel mit späterer Trade-Kompaktierung in Abschnitt C, weil Average-Cost nur Gesamtstückzahl & Gesamtkosten braucht, keine Einzel-Trade-Reihenfolge)
|
||
- [ ] Neue Domain-Struktur `TraderPosition` (TraderId, MarketOutcomeId, SharesHeld, AvgCost, RealizedPnl, zuletzt aktualisiert) — inkrementell fortschreibbar statt bei jeder Berechnung die komplette Trade-Historie neu zu scannen
|
||
- [ ] Buchungslogik je `TradeSide` sauber definieren:
|
||
- [ ] `Buy`: Shares += Size, AvgCost neu gewichten
|
||
- [ ] `Sell`: RealizedPnl += Size × (Price − AvgCost), Shares −= Size
|
||
- [ ] `Redeem` (Marktauflösung): RealizedPnl += verbleibende Shares × (1 oder 0 je nach Gewinn-Outcome − AvgCost), Shares = 0
|
||
- [ ] `Split` / `Merge`: als neutrale Positionsumwandlung behandeln (kein PnL-Effekt), nicht wie `Sell`
|
||
- [ ] `AddLiquidity` / `RemoveLiquidity`: getrennt von Trading-PnL betrachten (eigene Kategorie, fließt nicht in "Trading-Skill"-Bewertung ein)
|
||
- [ ] Unrealisierten PnL für offene Positionen berechnen: `Shares × (MarketOutcome.CurrentPrice − AvgCost)`
|
||
- [ ] Gesamt-PnL = realisiert + unrealisiert (ersetzt `OverallPnL`, `PnL30d/7d/24h` Felder in `TraderAnalytics`)
|
||
- [ ] Prüfen, ob wir zusätzlich/alternativ Polymarkets eigenen `/positions`-Endpoint (`GetTraderPositionsAsync`, liefert `CurrentValue`/`PercentPnl`) als Plausibilitäts-Check oder sogar als primäre Quelle für **offene** Positionen nutzen (aktuell komplett ungenutzt)
|
||
- [ ] `CalculatePnL`-Bug aus Abschnitt 0 im Zuge dessen mit erledigen
|
||
|
||
### A2. Win-Rate korrekt berechnen
|
||
- [ ] Win/Loss ist **pro Markt**, nicht pro Trade, definiert: ein Markt zählt als "Win", wenn der realisierte PnL aus diesem Markt (nach Redeem/vollständigem Exit) positiv ist
|
||
- [ ] `WinRate = Anzahl gewonnener Märkte / Anzahl abgeschlossener Märkte` (offene Positionen zählen nicht mit)
|
||
- [ ] Placeholder `return 0;` in `TraderAnalyticsWorker.CalculateWinRate` ersetzen
|
||
|
||
### A3. Deep-Dive-Kennzahlen mit echten Werten füllen
|
||
- [ ] **Voraussetzung klären:** Für Entry/Exit-Qualität und Timing-Accuracy brauchen wir eine **Preis-Historie** pro `MarketOutcome`, nicht nur den aktuellen Preis (`CurrentPrice`). Aktuell existiert keine Historisierung.
|
||
- [ ] Prüfen, ob Polymarkets CLOB-API einen Preishistorie-Endpoint (`/prices-history`) hergibt, den wir zum Backfill nutzen können
|
||
- [ ] Falls ja: neue Tabelle `MarketOutcomePriceSnapshot` (MarketOutcomeId, Timestamp, Price) einführen, periodisch befüllt (z.B. durch bestehenden `MarketSyncWorker`/`MarketHistoryWorker` erweitern)
|
||
- [ ] `AvgHoldDurationHours` echt berechnen: gewichtete Haltedauer zwischen Einstieg (Buy-Zeitpunkte, gewichtet nach Größe) und Ausstieg (Sell/Redeem) pro Position
|
||
- [ ] `EntryQuality`: Einstiegspreis im Vergleich zur nachfolgenden Preisentwicklung (z.B. Perzentil des Einstiegspreises innerhalb eines Zeitfensters danach)
|
||
- [ ] `ExitQuality`: analog für Ausstiegspreis
|
||
- [ ] `TimingAccuracy`: z.B. Anteil der Trades, die kurz vor einer für den Trader günstigen Preisbewegung platziert wurden
|
||
- [ ] Hardcodierte `50`-Neutralwerte in `AnalyticsService.PerformDeepDive` entfernen
|
||
|
||
### A4. Strategie-Klassifikation verbessern
|
||
- [ ] Zusätzliche Signale einbeziehen statt nur "Ø-Größe" und "Hedging-Rate":
|
||
- [ ] Verteilung der Haltedauern (kurz/lang, Varianz)
|
||
- [ ] Diversität der Marktkategorien
|
||
- [ ] Anteil der Trades kurz vor Marktauflösung vs. früh im Marktleben
|
||
- [ ] Nutzung gegenläufiger Positionen (Arbitrage-Muster über mehrere Outcomes/Märkte)
|
||
- [ ] Trade-Größen-Varianz (regelmäßig gleich große Orders = evtl. automatisiert)
|
||
- [ ] Überlegen, ob `StrategyType` als reines Einzel-Enum ausreicht oder ob wir zusätzlich Mehrfach-Signale/Tags parallel speichern (z.B. "primär Hedger, aber auch Whale-Größenordnung") — Enum bleibt als Primär-Tag, Zusatzsignale als eigene Felder/Scores
|
||
- [ ] Sicherstellen, dass nicht der Großteil der Trader dauerhaft bei "Unknown" landet (aktuell strukturell der Fall)
|
||
|
||
### A5. Copytrading-Eignungs-Score (neu)
|
||
- [ ] Neue Metrik-Dimension definieren, unabhängig vom bestehenden `PriorityScore`:
|
||
- [ ] **Liquiditäts-Fit**: durchschnittliche Positionsgröße im Verhältnis zur Marktliquidität/zum Volumen zum Handelszeitpunkt (Slippage-Risiko für Nachahmer)
|
||
- [ ] **Reaktionsfenster**: wie viel Zeit bliebe einem Copytrader realistisch zum Nachziehen (Trader mit Sekunden/Millisekunden-Kadenz sind nicht kopierbar)
|
||
- [ ] **Frequenz/Konzentration**: sehr hochfrequente/bot-artige Trader senken den Score automatisch
|
||
- [ ] **Track-Record-Länge & Konsistenz**: mehr abgeschlossene Märkte mit konsistent positivem PnL = höheres Vertrauen
|
||
- [ ] Kombinierten `CopytradingScore` (0–100) berechnen und persistieren (neues Feld auf `TraderScore` oder eigene Entity `TraderCopytradingProfile`)
|
||
- [ ] Im Dashboard/API sichtbar machen (getrennt von der bisherigen "Priorität", da unterschiedliche Fragestellung: *gut* vs. *kopierbar*)
|
||
|
||
### A6. (siehe Abschnitt 0) TradeSide-Bugfix in PnL-Berechnung
|
||
- [ ] Erledigt sich durch A1, hier nur als Häkchen zur Nachverfolgung
|
||
- [ ] Kurzer Änderungsvermerk/Commit-Hinweis, damit klar ist, dass dieser Bug bewusst behoben wurde
|
||
|
||
---
|
||
|
||
## Phase 2 – Robustheit & Wartbarkeit
|
||
|
||
### B1. EF Core Migrationen einführen
|
||
- [x] Aktuellen Ist-Stand des Schemas exakt erfassen (inkl. aller manuellen `ALTER TABLE`-Patches aus `DependencyInjection.cs`) — abgeglichen, Entities/`OnModelCreating` deckten den Patch-Stand bereits vollständig ab
|
||
- [x] Erste **Baseline-Migration** erzeugt (`20260701102311_InitialBaseline`, in `src/Predictalytics.Infrastructure/Migrations/`), entspricht exakt dem aktuellen Schema
|
||
- [x] `EnsureCreatedAsync` + handgeschriebene `ExecuteIfColumnMissing`/`ALTER`-Helfer aus `DependencyInjection.cs` entfernt, durch `db.Database.MigrateAsync()` ersetzt (Platform-Seed bleibt als `INSERT IGNORE`)
|
||
- [x] Migration end-to-end gegen eine echte, frische DEV-Datenbank getestet (`lqf7.your-database.de`/`bergisnu_db0`, vom Nutzer bereitgestellt) — `dotnet ef database update` lief fehlerfrei durch, `dotnet ef migrations list` bestätigt sie als angewendet
|
||
- [x] `AppDbContextFactory` (Design-Time-Factory für `dotnet ef`) liest Zielverbindung jetzt aus Env-Vars (`PREDICTALYTICS_DB_SERVER/_NAME/_USER/_PASSWORD`, via `MySqlConnectionStringBuilder` statt roher String-Konkatenation, da Passwörter Sonderzeichen wie `;`/`{}` enthalten können) statt fest codiert — kein Secret mehr im Repo nötig, um Migrationen gegen eine beliebige DB zu fahren
|
||
- [x] ~~Live-Produktions-DB manuell als "bereits migriert" markieren~~ — vorerst zurückgestellt: Da wir laut Abschnitt D ohnehin einen kompletten Neustart der Produktions-DB planen (Altdaten sind jederzeit nachladbar, siehe C0), richten wir die neue Produktions-DB am Ende genauso ein wie die DEV-DB (leer anlegen + `dotnet ef database update`) — der fummelige Stamp-Schritt auf die bestehende Live-DB entfällt dann komplett. Nur falls wir uns doch gegen den Neustart entscheiden, müsste dieser Schritt nachgeholt werden
|
||
- [ ] Alle künftigen Schemaänderungen aus Phase 1 (z.B. `TraderPosition`, `MarketOutcomePriceSnapshot`, `TraderCopytradingProfile`) als reguläre Migrationen anlegen — direkt gegen die DEV-DB entwickeln/testen, da wir dort laut Nutzer frei experimentieren dürfen
|
||
|
||
### B2. Secrets-Management (Release-Vorbereitung, kein akuter Risikofall)
|
||
> Software läuft aktuell nur lokal, kein Fremdzugriff — Passwort bleibt vorerst wie es ist, keine Rotation nötig. Dieser Punkt ist reine **Vorbereitung**, damit das Projekt bei Bedarf später auch ohne die aktuellen Secrets veröffentlicht/geteilt werden könnte, ohne den Code nochmal anfassen zu müssen. Dadurch niedrigere Priorität als vorher angenommen — kann später in der Reihenfolge stehen.
|
||
- [ ] Produktiven MySQL-Connection-String aus `appsettings.json` in Umgebungsvariablen/User Secrets verschieben
|
||
- [ ] `appsettings.json` im Repo künftig nur Platzhalter/Dev-Default enthalten, damit ein Klon des Repos ohne die echten Zugangsdaten lauffähig bleibt (mit eigener lokaler DB)
|
||
- [ ] Keine Passwort-Rotation nötig, solange rein lokale Nutzung
|
||
|
||
### B3. Scoring-Pipeline entkoppeln
|
||
- [ ] `PollingWorker` soll **nicht** bei jedem 60-Sekunden-Zyklus alle Trader neu bewerten
|
||
- [ ] Nur Trader neu bewerten, die seit letzter Berechnung neue Trades bekommen haben (Dirty-Flag oder Vergleich `LastTradesUpdatedAt` vs. `TraderScore.CalculatedAt`)
|
||
- [ ] N+1-Datenbankzugriffe in `ScoringService.RecalculateAllScoresAsync` durch Batch-Queries ersetzen
|
||
- [ ] 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)
|
||
- [ ] **`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
|
||
- [ ] **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
|
||
- [ ] **`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 (<geholte Batch-IDs>)` abfragen statt der gesamten Historie
|
||
- [ ] **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
|
||
- [ ] **`TradeHistoryWorker` bricht die Trade-Verarbeitung beim ersten bekannten Trade ab** (`if (!isInitial) break;`), verlässt sich also darauf, dass die API immer streng neueste-zuerst liefert. `PollingWorker`s äquivalente Schleife macht das NICHT. Sollte angeglichen werden — der Performance-Gewinn ist gering gegenüber dem Risiko einer stillen Datenlücke, falls die Annahme mal nicht stimmt
|
||
|
||
### B4. Testabdeckung für die kritische Logik
|
||
- [ ] Neues Testprojekt (z.B. `Predictalytics.Application.Tests`) anlegen — aktuell existiert **kein einziges** Testprojekt
|
||
- [ ] Unit-Tests für die neue PnL-Engine (A1) — insbesondere Grenzfälle: nur offene Position, nur geschlossene Position, Split/Merge, Redeem-Verlust vs. -Gewinn
|
||
- [ ] Unit-Tests für Win-Rate (A2)
|
||
- [ ] Unit-Tests für Deep-Dive-Kennzahlen (A3) und Copytrading-Score (A5)
|
||
- [ ] Diese Tests idealerweise **parallel zu A1–A5** schreiben, nicht erst am Ende nachziehen
|
||
|
||
### B5. Kleine verwandte Aufräumarbeit
|
||
- [ ] Log-Rotation/Retention der Serilog-Datei-Sinks (`WinFormsHost/logs`) prüfen — wächst potenziell unbegrenzt, ähnliches Prinzip wie die DB-Speicherplatzfrage unten
|
||
|
||
---
|
||
|
||
## Datenbank-Speicherplatz-Optimierung
|
||
|
||
### C0. Kritische Bewertung deiner beiden Ideen (überarbeitet nach Diskussion 2026-07-01)
|
||
|
||
**Neue Rahmenbedingungen, die die Bewertung ändern:**
|
||
- Alle Rohdaten sind jederzeit erneut abrufbar — über Polymarkets `/activity`-Endpoint ([Doku](https://docs.polymarket.com/api-reference/core/get-user-activity), keine sichtbare Limitierung), im Zweifel über die Blockchain selbst. Wir müssen also **nichts "für immer" sichern** — Löschen ist kein unwiderruflicher Datenverlust.
|
||
- Analyseziel ist laut Auftrag explizit die **aktuelle** Strategie und aktueller Erfolg/Misserfolg, nicht die Historie von vor Jahren. Wir brauchen also gar keine unbegrenzte Detailtiefe — nur genug, um "aktuelles Verhalten" zu charakterisieren.
|
||
- Kern-Einsicht aus A1: Sobald ein Trade in `TraderPosition` (SharesHeld/AvgCost/RealizedPnl) eingerechnet ist, wird die **Rohzeile für die PnL-Fortführung nie wieder gebraucht** — die Position ist bereits die komprimierte Zusammenfassung. Rohdaten braucht es nur noch für die Strategie-/Deep-Dive-Analyse (Haltedauer, Timing, Hedging-Muster), und die soll ohnehin nur das aktuelle Fenster betrachten.
|
||
|
||
→ Das ersetzt die alte Idee 1 (Archivierung nur inaktiver Trader + Reaktivierungs-Baseline) durch ein einfacheres, einheitliches Prinzip: siehe **C1 – Rollierendes Zeitfenster**.
|
||
|
||
**Idee 2 (Hochfrequenz-Trader alle 10 Min. aggregieren)** bleibt weiterhin sinnvoll — allerdings in überarbeiteter Form, da die ursprüngliche Beschreibung zu grob war:
|
||
- "Min/Max/Ø Buy-In" ohne Trennung nach Buy/Sell und nach Markt/Outcome zerstört genau die Information, die die PnL-Engine (A1) braucht.
|
||
- Eine feste 10-Minuten-Uhrzeit-Bucket-Grenze für *alle* Trades eines als "Bot" eingestuften Traders würde auch die wenigen möglicherweise bedeutsamen Trades eines Mischtyps mit-aggregieren.
|
||
- Relevant bleibt sie, weil auch *innerhalb* des neuen rollierenden Zeitfensters (siehe C1) ein aktiver Bot enorme Mengen an Trades erzeugen kann — das ist jetzt eine Optimierung fürs "heiße" Fenster, nicht mehr fürs Langzeitarchiv.
|
||
|
||
→ Vorschlag: **Burst-Erkennung statt starrer Zeit-Buckets** (nur tatsächlich dichte Trade-Sequenzen kompaktieren) und **Aggregation getrennt nach Trader+Markt/Outcome+Seite** mit **VWAP** (mengengewichteter Durchschnittspreis) statt einfachem Durchschnitt — bleibt unter der Average-Cost-Methode aus A1 nahezu verlustfrei für die PnL-Berechnung.
|
||
|
||
### C1. Rollierendes Zeitfenster statt Archivierung (ersetzt die alte "Idee 1")
|
||
- [ ] Konfigurierbares Retention-Fenster einführen (Default-Vorschlag: 3–6 Monate) — gilt **für alle Trader gleichermaßen**, nicht nur für inaktive. Länge sollte sich daran orientieren, wie weit die Deep-Dive-/Strategie-Analyse (A3/A4) tatsächlich zurückschaut, um "aktuelle Strategie" zu charakterisieren
|
||
- [ ] Reihenfolge pro Trade zwingend einhalten: **erst** in `TraderPosition`/Monats-Aggregat (s.u.) einrechnen und das sicher persistieren, **dann erst** die Rohzeile löschen — rein zeitbasiert, unabhängig davon ob die betroffene Position noch offen oder schon geschlossen ist
|
||
- [ ] Kein Export/Cold-Storage nötig, da jederzeit über die Polymarket-API bzw. im Zweifel über die Blockchain nachladbar — vereinfacht C1 gegenüber der ursprünglichen Idee erheblich (keine Baseline-Felder, keine Reaktivierungs-Sonderfälle)
|
||
- [ ] Optionales, leichtgewichtiges Langzeit-Signal (nice-to-have, niedrige Priorität): ein grobes Monats-Aggregat pro Trader (Monat, realisierter PnL, Trade-Anzahl, Volumen) für einen "war er über Monate hinweg konsistent profitabel"-Trend im Copytrading-Score (A5) — **ohne** Trade-Detailtiefe, nur wenige Kennzahlen pro Monat
|
||
- [ ] Neuer periodischer Cleanup-Job (`TradeRetentionWorker`), der Trades außerhalb des Fensters findet und löscht, nachdem die Voraussetzung (Position/Aggregat aktuell) erfüllt ist
|
||
- [ ] Kurzer Stichprobentest, wie weit `/activity` pro Wallet tatsächlich zurückreicht (bestätigt/verifiziert nur die schon vorliegende Doku-Aussage, geringe Priorität da schon durch Nutzer-Recherche plausibilisiert)
|
||
|
||
### C2. Kompaktierung von Hochfrequenz-Tradern innerhalb des Zeitfensters (überarbeitete "Idee 2")
|
||
- [ ] Burst-Erkennung statt globalem Zeitraster: Sequenz von Trades mit Abstand kleiner als Schwellwert (z.B. 60s, konfigurierbar) zwischen aufeinanderfolgenden Trades **desselben Traders, Outcomes und derselben Seite (Buy/Sell)** gilt als "Burst"
|
||
- [ ] Mindestlänge für Kompaktierung festlegen (z.B. erst ab 20+ Trades im Burst lohnt sich das)
|
||
- [ ] Bestehende Bot-Heuristik (`intervals.Average() < 10` in `AnalyticsService`) als Ausgangspunkt wiederverwenden/verallgemeinern statt eine zweite, unabhängige Definition einzuführen
|
||
- [ ] Aggregat-Datensatz pro Burst: Anzahl Trades, Summe Size, Summe Amount, Min-Preis, Max-Preis, **VWAP** (nicht einfacher Durchschnitt)
|
||
- [ ] Design-Entscheidung: Aggregat als zusätzliche nullable Spalten auf der bestehenden `Trade`-Tabelle (`AggregateCount`, `AggregateMinPrice`, `AggregateMaxPrice`) statt separater Tabelle — bestehender Code (PnL, Deep-Dive) muss dadurch kaum angepasst werden, ein Aggregat-Datensatz ist einfach ein "Trade" mit `Size = Summe`, `Price = VWAP`
|
||
- [ ] **Reihenfolge beachten:** Diese Kompaktierung erst implementieren, nachdem A1 (neue PnL-Engine) steht und validiert ist — sonst kompaktieren wir Daten weg, bevor wir wissen, was die neue Engine wirklich braucht
|
||
|
||
### C3. Weitere eigene Vorschläge
|
||
- [ ] **Spaltentypen verkleinern**: `TransactionHash`, `MarketId`, `AssetId` sind Hex-/Dezimal-Strings fester Länge (z.B. `0x` + 64 Hex-Zeichen) — als `BINARY(32)` statt `VARCHAR(66/80)` speichern spart ca. 30–50% Platz auf diesen stark indizierten Spalten und ist schneller
|
||
- [ ] **Tabellen-Partitionierung** von `Trades` nach Monat (`ExecutedAt`) prüfen — erlaubt später das Archivieren/Droppen ganzer Partitionen statt teurer zeilenweiser `DELETE`s
|
||
- [ ] **Materialitätsschwelle**: Mikro-Trades unterhalb eines Betrags (z.B. < $0.50) unabhängig von Bot-Klassifizierung direkt aggregiert erfassen, da sie für Copytrading/Strategieanalyse ohnehin kaum Aussagekraft haben
|
||
- [ ] ~~Kaltarchiv außerhalb der DB~~ — nicht mehr nötig: da Rohdaten jederzeit über die API/Blockchain nachladbar sind (siehe C1), erübrigt sich ein separates Cold-Storage-Export. Nur falls sich die API-Nachladbarkeit später als doch eingeschränkt herausstellt, hier nochmal aufgreifen
|
||
- [ ] **InnoDB-Kompression** (`ROW_FORMAT=COMPRESSED`) für die `Trades`-Tabelle als schneller Zwischenschritt prüfen — kein Datenverlust, kombinierbar mit allem anderen
|
||
- [ ] `Split`/`Merge`/`AddLiquidity`/`RemoveLiquidity`-Ereignisse (siehe A1) ggf. separat und kompakter ablegen, da sie für die Trader-Bewertung meist weniger relevant sind als `Buy`/`Sell`
|
||
|
||
### C4. Abhängigkeit zu Phase 1
|
||
- [ ] Merksatz: **Erst A1 (neue PnL-Engine) fertigstellen, dann C1/C2 umsetzen.** Sonst laufen wir Gefahr, Rohdaten wegzuoptimieren, die die neue Engine noch gebraucht hätte.
|
||
|
||
---
|
||
|
||
## Entscheidung: Frischer Datenbank-Neustart nach Abschluss?
|
||
|
||
**Neu bewertet (2026-07-01):** Da wir laut C0/C1 ohnehin nichts an Alt-Historie "für immer" brauchen und alles bei Bedarf über die API/Blockchain nachladbar ist, ist ein sauberer Reset am Ende von Phase 1+2 **risikoarm und klar empfehlenswert** — nicht mehr nur eine Option mit Vorbehalt.
|
||
|
||
### D1. Kurzer Bestätigungs-Check (geringe Priorität, kein Blocker mehr)
|
||
- [ ] Stichprobenartig verifizieren, dass `/activity` für ein bekanntes, lange aktives Wallet tatsächlich vollständig zurückreicht — bestätigt nur die bereits über die [Doku](https://docs.polymarket.com/api-reference/core/get-user-activity) plausibilisierte Annahme, ist kein Show-Stopper mehr für die Entscheidung
|
||
|
||
### D2. Ablauf des Neustarts
|
||
- [ ] Vollständiges Backup/Dump der aktuellen Datenbank sichern (reine Vorsichtsmaßnahme, wird voraussichtlich nicht gebraucht) und einige Wochen aufbewahren
|
||
- [ ] EF-Migrationen-Baseline + alle Phase-1/2-Schemaänderungen fertigstellen (B1)
|
||
- [ ] Neue leere Datenbank anlegen, Migrationen anwenden
|
||
- [ ] Liste bereits bekannter Trader-Wallets aus dem alten Bestand als Startpunkt für die Re-Discovery übernehmen
|
||
- [ ] Re-Import über die Worker-Pipeline (mit korrigierter PnL-Logik, direkt im Rahmen des neuen Retention-Fensters aus C1) laufen lassen
|
||
- [ ] **Wichtig:** Dieser Schritt kommt ganz am Ende von Phase 1 + 2 — nicht vorher, damit wir nicht zweimal migrieren/importieren müssen
|
||
|
||
---
|
||
|
||
## Empfohlene Reihenfolge (Kurzfassung, Stand 2026-07-01)
|
||
|
||
1. **B1** – EF-Migrationen-Baseline einführen (Fundament für alle weiteren Schemaänderungen)
|
||
2. **A1 + A6** – Positionsbasierte PnL-Engine inkl. TradeSide-Bugfix
|
||
3. **A2** – Win-Rate
|
||
4. **A3** – Deep-Dive-Kennzahlen (inkl. Preis-Historie-Voraussetzung klären)
|
||
5. **A4** – Strategie-Klassifikation verbessern
|
||
6. **A5** – Copytrading-Eignungs-Score
|
||
7. **B4** – Tests (idealerweise begleitend zu 2.–6., hier als Nachhol-Punkt falls übersprungen)
|
||
8. **B3** – Scoring-Pipeline entkoppeln/optimieren
|
||
9. **C1 + C2 + C3** – Speicherplatz-Optimierung: rollierendes Zeitfenster + Burst-Kompaktierung (bewusst erst jetzt, siehe C4)
|
||
10. **D** – Datenbank-Neustart durchführen (ganz am Ende, jetzt als klar empfohlener Schritt statt offener Entscheidung)
|
||
|
||
(B2 – Secrets-Vorbereitung und B5 – Log-Rotation sind risikoarme Nebenpunkte ohne akute Dringlichkeit, da rein lokale Nutzung; können jederzeit zwischendurch erledigt werden, wenn Zeit ist.)
|