feat: implement Part D and E from FIXPLAN
- D1/D2/D2c: Added TraderTraits entity, TraderTraitCalculator, Market Return Metrics (MedianWin, AvgWin, etc.), and trait filters
- D3: Implemented HF-Trader Tiering via IngestMode (Full, Aggregated, SnapshotOnly) and updated TradeHistoryWorker to respect tiers
- E1-E5: Added MasterStatus to Trader, TraderWindowMetrics for rolling analytics, Fingerprint metrics (PriceBandProfile, P50/P90), Copyability aggregates (Volume, Drift, Edge)
- E6: Implemented GET /api/traders/{id}/profile and GET /api/traders/correlation
- Replaced FIXPLAN-2026-07-09.md with FIXPLAN-TODO.md and FIXPLAN-DONE.md
- Cleaned up API docs and plan to use generic terms (removed hardcoded PolyTrader references)
- Added respective EF Core Migrations
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Fix- und Datenreparatur-Plan (Stand 2026-07-09, Übergabe an Gemini)
|
||||
|
||||
> **Abnahmekriterium für alle Code-Änderungen:** `dotnet test src/Predictalytics.Application.Tests` muss
|
||||
> **16 grün + 1 übersprungen** liefern (der Skip `CheckpointResetAndReplay_DoesNotDoubleCountBalance` ist eine
|
||||
> dokumentierte, bewusste Entscheidung). Die Assertions der Invarianten-Tests dürfen **nicht** verändert werden —
|
||||
> sie definieren das Soll-Verhalten. Wenn ein Test rot wird, ist der Code falsch, nicht der Test.
|
||||
|
||||
## Hintergrund
|
||||
|
||||
Die Engine-Fixes vom 09.07. sind korrekt (Tests grün). Die im WebUI sichtbaren Probleme haben drei andere Ursachen:
|
||||
|
||||
1. Die Buttons der **Trader-Detailseite** nutzen alte, Job-lose Endpoints (die Listen-Buttons nutzen bereits das Job-System).
|
||||
2. Die **abgeleiteten Daten in der DB stammen aus der Bug-Ära** (Snapshots/Positionen wurden von den alten, fehlerhaften
|
||||
Engine-Versionen berechnet). Beispiel aus dem Live-System: `PnL30d = 244,0K` bei `TotalPnL = 158,5K`, weil der
|
||||
Basis-Snapshot `-85,5K` enthält (korrupter Altwert). Kein Code-Fix ändert das — die Daten müssen einmalig repariert werden.
|
||||
3. **Deadlocks + Shutdown-Fehlerkaskaden** in den Workern (unbatchtes Reconciliation-UPDATE, fehlende Cancellation-Behandlung).
|
||||
|
||||
**Ein DB-Reset ist NICHT nötig.** Die Rohdaten (`Trades`) sind größtenteils intakt; Positionen, Analytics, Snapshots und
|
||||
Scores sind abgeleitet und lokal neu berechenbar. Nur Trader, deren Alt-Trades die Retention bereits gelöscht/kompaktiert
|
||||
hat, brauchen einen gezielten API-Re-Import (kleine Teilmenge, siehe Teil B).
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Teil D — Ausbaustufe: Merkmals-Tags & HF-Trader-Tiering (ergänzt 2026-07-11)
|
||||
|
||||
> **Bereits direkt erledigt (nicht Teil dieses Auftrags):** `TotalTrades` wird jetzt von der Engine aus dem
|
||||
> echten Row-Count gesetzt; der Kategorie-Mapper klassifiziert zusätzlich über den Frage-Text und matcht kurze
|
||||
> Tokens nur an Wortgrenzen; `UpdateMarketFields` überschreibt gute Kategorien nicht mehr mit "Other".
|
||||
> Teststand: **32 grün + 1 Skip** — das ist die neue Basis, Assertions unverändert lassen.
|
||||
|
||||
### D3. Trader-Tiering (`IngestMode`) — Umgang mit Ultra-HF-Tradern (RN1, Swisstony)
|
||||
|
||||
**Hintergrund:** Ultra-HF-Trader werden heute schon NICHT vollständig erfasst (PollingWorker: 100 Trades/60 s
|
||||
gegen 300+/min) — das Trade-Replay-PnL ist für diese Klasse bereits falsch und frisst nur Speicher.
|
||||
|
||||
- Enum `IngestMode { Full = 0, Aggregated = 1, SnapshotOnly = 2 }` + Spalte auf `Trader` (Default Full), Migration.
|
||||
- **Klassifizierung** im `TradeHistoryWorker` nach jedem Fetch: Zeitspanne der letzten 500 Trades →
|
||||
Trades/Tag-Schätzung. > 5.000/Tag → SnapshotOnly; > 100/Tag → Aggregated. Hysterese: Rückstufung Richtung
|
||||
Full erst nach 7 Tagen unter der halben Schwelle (kein Flattern).
|
||||
- **SnapshotOnly (Tier C):**
|
||||
- Polling/History-Worker überspringen den Trade-Import komplett.
|
||||
- Stündlich: `GetTraderPositionsAsync` (der ungenutzte `/positions`-Endpoint!) → `TraderPositions` upserten
|
||||
(size→SharesHeld, avgPrice→AvgCost, cashPnl→RealizedPnl); `OverallPnL` aus Positions +
|
||||
`GetLeaderboardAsync`-PnL für die Zeitfenster; `TraderDailySnapshot` weiter schreiben (Equity-Kurve bleibt).
|
||||
- Wöchentliche „Biopsie": einmal 500 Trades via /activity ziehen, NUR durch den `TraderTraitCalculator`
|
||||
schicken, NICHT persistieren.
|
||||
- Engine überspringt Trade-Replay für SnapshotOnly; Estimator/Enrichment überspringen; CopytradingScore = 0
|
||||
mit Trait `not_copyable_hf`.
|
||||
- **Aggregated (Tier B):** Aggregation beim Import statt nachträglicher Kompaktierung: Bucket
|
||||
(TraderId, MarketOutcomeId, Side, Stunde) mit VWAP-Preis, Summen-Size/-Amount, `AggregatedCount`; gespeichert
|
||||
als normale Trade-Zeile mit `PlatformTradeId = "AGG_{traderId}_{outcomeId}_{side}_{yyyyMMddHH}"`, laufende
|
||||
Stunde per Upsert aktualisieren. Average-Cost-Engine bleibt damit verlustfrei.
|
||||
- Danach: `RetentionDays` für Full-Trader auf 180 erhöhen (Config) — die Bots stellen nicht mehr die Masse,
|
||||
und längerer Track-Record nützt genau den kopierbaren Tradern.
|
||||
- **Tests:** Klassifizierungs-Schwellen + Hysterese als pure Funktion; PollingWorker importiert für
|
||||
SnapshotOnly-Trader nichts; Aggregations-Upsert ist idempotent (2× dieselbe Stunde → 1 Zeile, korrekte Summen
|
||||
und `AggregatedCount`).
|
||||
|
||||
### D4. Abnahme Teil D
|
||||
|
||||
1. `dotnet test`: alle bestehenden **32 + 1 Skip** bleiben grün (Assertions unverändert) + die neuen D-Tests.
|
||||
2. RN1/Swisstony stehen nach der Einstufung auf SnapshotOnly: PnL gefüllt (aus /positions/Leaderboard),
|
||||
Traits gesetzt, **keine neuen Trade-Zeilen** mehr in der DB.
|
||||
3. Detailseite zeigt Trait-Chips; Traders-Liste filterbar nach Trait.
|
||||
3b. Detailseite zeigt Median-Win/-Loss-Rendite und Profit Factor; die D2c-Werte sind für Trader mit
|
||||
abgeschlossenen Märkten gefüllt.
|
||||
4. Tägliches DB-Wachstum sichtbar reduziert (DB-Size-Anzeige im WinForms-Statusbar beobachten).
|
||||
|
||||
Reihenfolge: **D1 → D2/D2b/D2c → D3** (D2c ist klein und gehört in denselben Engine-Durchlauf wie die Winrate; bei D3 zuerst Tier C, dann Tier B).
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user