Files
RichardandClaude Opus 5 6975720dc6 Eine Roadmap statt neun Plandokumente; alte Plaene ins Archiv
Die offenen Punkte lagen ueber neun Dokumente verstreut, teils widersprechend,
teils mit Punkten, die laengst umgesetzt waren. ROADMAP.md fuehrt sie zusammen:
sechs Stufen, jeder Punkt gegen den Code geprueft.

Markierung ueber eine Legende, damit Konzepte nicht mit Aufgaben verwechselt
werden: dringend, eingeplant, Backlog, Konzept (durchdacht, aber bewusst nicht
eingeplant), liegt beim Nutzer, verworfen.

Stufen: 0 Sofort (OpenRouter-Key) - 1 Aufraeumen abschliessen (Merge nach main,
Nullable-Warnungen, Startup-Backfill) - 2 Portierung abschliessen (Linux-Erstlauf
und Verifikation, dann Deployment/CI) - 3 Analytik schaerfen (Strategie-
Klassifikation, Backtest-Harness, Track-Record im Score, Ranking) - 4 Daten-
haushalt (SQL des Nutzers) - 5 Ingest skalieren - 6 Monetarisierung vorbereiten.

Ein Anhang haelt fest, was geprueft und bewusst NICHT auf die Roadmap kam, damit
es nicht versehentlich wieder als Aufgabe auftaucht (Azuro/Limitless, die
Marketing-Seiten, Kaltarchiv, EF Core 10).

Archiv: FIXPLAN-DONE, FIXPLAN-G-Speicher, FIXPLAN-TODO, FIXPLAN-UI-Ranglisten,
UMSETZUNGSPLAN sowie ANALYSE-Linux-Portierung, PLAN-Linux-Portierung,
PLAN-Architektur-WebUI-Backend und PLAN-DatenIngest-Skalierung liegen jetzt
unter docs/archiv/ mit einer README, die jedes Dokument einordnet. Sie bleiben
als Begruendungs- und Detailquelle - die Roadmap nennt jeden Punkt knapp, die
Herleitung steht dort.

Dabei aufgefallen: die beiden nie gepflegten Plaene (Architektur, DatenIngest)
zeigten 40 offene Punkte, von denen die Haelfte umgesetzt war - Read/Control-
Split, /api/capabilities, CORS-Whitelist, Egress-Kanaele mit Cooldown und
Per-Kanal-Limiter, Ingest-Tiering. Nachgeprueft, abgehakt und mit Statusblock
eingeordnet, sonst waere das Archiv selbst eine Fehlerquelle.

Was dabei nur teilweise umgesetzt war, ist als Roadmap 5.3 aufgenommen
(Health-Statistik je Kanal, multi-homed pruefen, DB-Guard, Plausibilitaets-
pruefung), ebenso der nie durchgefuehrte Audit auf versteckte Writes in
Read-Endpunkten (6.1). Header-Rotation ist als verworfen markiert statt offen
zu bleiben: Verschleierung gegenueber Polymarket riskiert genau den Zugang, auf
dem das Projekt aufsetzt.

STATUS.md beschreibt jetzt nur noch den Ist-Stand und verweist fuer die offenen
Punkte auf die Roadmap, damit nichts doppelt gepflegt wird. CLAUDE.md nennt
beide Einstiege.

Build gruen, 126 Tests gruen, keine toten Links.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 18:46:00 +02:00

26 KiB
Raw Permalink Blame History

Predictalytics Umsetzungsplan (Phase 1 & 2 + Storage-Optimierung)

Stand 2026-08-23 — Phase 1 und 2 sind bis auf zwei Punkte umgesetzt

Die Häkchen waren bis dahin nie nachgezogen worden: 85 Punkte standen auf offen, obwohl der Code sie längst enthielt. Am 2026-08-23 abschnittsweise gegen den Code geprüft und gesetzt. Belege:

Belegt durch
A1 PnL-Engine Infrastructure/Services/PositionPnLEngine.cs — alle TradeSide-Zweige einzeln behandelt (:157-217), Split/Merge neutral, unrealisiert aus CurrentPrice AvgCost (:266), Gesamt-PnL :290. Entity Domain/Entities/TraderPosition.cs. Tests: PositionPnLEngineTests.cs
A2 Win-Rate CalculateMarketWinRates (PositionPnLEngine.cs:381) — marktbasiert, kein return 0 mehr
A3 Deep-Dive MarketOutcomePriceSnapshot existiert und wird ausgewertet (AnalyticsService.cs:88,390). Die 50 ist jetzt Rückfallwert bei fehlender Preishistorie (:456,457,474), kein fester Neutralwert mehr
A5 Copytrading-Score Infrastructure/Services/CopytradingEstimator.csabweichend umgesetzt und besser als geplant: statt geschätztem Liquiditäts-Fit misst der Score den echten Alpha-Verfall gegen das Markt-Tape (Slippage bei 10 s Folgelatenz, 70 %) plus Sizing-Konsistenz (30 %). Frequenz/Konzentration deckt stattdessen IngestMode + Trait not_copyable_hf ab. Track-Record-Länge fließt nicht in den Score ein
B1/B4 32 EF-Migrationen; Testprojekt existiert, 126 grün + 1 Skip
B3 ScoringService.RecalculateAllScoresAsync:127 rechnet nur bei CalculatedAt < LastTradesUpdatedAt neu, Rank-Update gebatcht; eigener ScoringAndAlertsWorker mit 15-Minuten-Takt
B5 LoggingSetup.cs:90-133retainedFileCountLimit und fileSizeLimitBytes gesetzt
B6 Alle fünf Bugs behoben: geschlossene Märkte nur noch 1×/Tag (MarketSyncWorker.cs:52), Watchlist von der Löschung ausgenommen (TraderRepository.cs:137), GetKnownPlatformTradeIdsAsync filtert auf die Batch-IDs (TradeRepository.cs:158), _syncSemaphore in AddOrUpdateAsync (MarketRepository.cs:31), break durch einen Filter ersetzt (TradeHistoryWorker.cs:200)
C1/C2 TradeRetentionWorker + Application/Services/StorageGovernor.cs (budgetabhängiges Fenster), Burst-Kompaktierung ab TradeRetentionWorker.cs:148

Was wirklich offen ist

  1. A4 — Strategie-Klassifikation (Zeilen 4754). Der einzige größere unerledigte Block aus Phase 1. StrategyType entsteht weiterhin aus genau zwei Signalen (AnalyticsService.cs:418 und, dupliziert, ScoringService.cs:89): avgSize > 10000 → Whale, hedgingRate > 30 → Hedger, sonst Bot/Unknown. Genau der Zustand, den A4 beheben sollte — der Großteil der Trader landet strukturell auf Unknown. Die Bausteine dafür liegen bereits fertig herum (StrategyMetricsCalculator, TraderTraitCalculator, FingerprintSnapshotService), sie speisen die Klassifikation nur nicht. Die Doppelung gehört beim Anfassen zusammengeführt.
  2. B2 — Secrets (Zeilen 8486). Nicht mehr „kein akuter Risikofall": in src/Predictalytics.Api/appsettings.json steht ein echter OpenRouter-API-Key im Klartext, versioniert seit Commit 7045002. Siehe Warnung unten.
  3. C3 (148153) und D1/D2 (165173) sind DB-Arbeiten auf SQL-Ebene bzw. die Entscheidung über den Datenbank-Neustart — beides liegt beim Nutzer, nicht im Code.
  4. C1, Zeile 137: Stichprobentest, wie weit /activity zurückreicht. Eine manuelle Prüfung, die sich im Code nicht belegen lässt — bewusst offen gelassen.

⚠️ Sicherheit — Handlungsbedarf

src/Predictalytics.Api/appsettings.json enthält den OpenRouter-API-Key im Klartext und liegt so im Git-Verlauf und auf dem Gitea-Server. Den Schlüssel bei OpenRouter zurückziehen und neu ausstellen — ihn nur aus der Datei zu löschen hilft nicht, die Historie behält ihn. Danach über die Umgebungsvariable OpenRouter__ApiKey setzen; WebApplication.CreateBuilder liest Umgebungsvariablen bereits mit Vorrang vor appsettings.json, dafür ist keine Codeänderung nötig.

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 (0100) 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

  • 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
  • Erste Baseline-Migration erzeugt (20260701102311_InitialBaseline, in src/Predictalytics.Infrastructure/Migrations/), entspricht exakt dem aktuellen Schema
  • EnsureCreatedAsync + handgeschriebene ExecuteIfColumnMissing/ALTER-Helfer aus DependencyInjection.cs entfernt, durch db.Database.MigrateAsync() ersetzt (Platform-Seed bleibt als INSERT IGNORE)
  • 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
  • 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
  • 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) — 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. PollingWorkers ä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 A1A5 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, 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: 36 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. 3050% 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 DELETEs
  • 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 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.)