Add Teil F (structural category-detection fix) to FIXPLAN-TODO

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-16 20:20:05 +02:00
co-authored by Claude Opus 4.8
parent 16431f38a5
commit 04596f95a6
+77
View File
@@ -71,3 +71,80 @@ Reihenfolge: **D1 → D2/D2b/D2c → D3** (D2c ist klein und gehört in denselbe
--- ---
---
## Teil F — Kategorie-Erkennung strukturell reparieren (ergänzt 2026-07-13)
> Nutzer meldet: Markt-Kategorien und die Kategorien, in denen sich ein Trader bewegt, stimmen
> weiterhin nicht. Ursache **live gegen die Gamma-API verifiziert** — es ist strukturell, nicht der Mapper.
### F0. Root Cause (verifiziert)
- Die **`/events`-Liste** (MarketSyncWorker-Pfad) liefert **reichhaltige Tags**
(z. B. `['Sports','Soccer','FIFA World Cup',...]`).
- Der **`/markets?condition_id=`-Pfad** (On-Demand, `PolymarketProvider.GetMarketAsync`, aufgerufen
vom `PollingWorker` für jeden noch unbekannten Markt) liefert **weder `category` noch Event-Tags**
(`raw.Events[0].Tags` ist leer, auch mit `include_tag=true`). → Solche Märkte werden nur per
Frage-Text klassifiziert und landen sonst auf **`Other`**.
- **Genau die vom Trader gehandelten Märkte entstehen überwiegend on-demand** → viele `Other`.
- Verschärfend: **geschlossene/aufgelöste** Märkte deckt der aktive Events-Sync nicht laufend ab →
historische Märkte (die für die Analyse zählen) bleiben ohne Tags = `Other`.
- Zwei Folgefehler: (a) `GetSubcategory` nimmt **`tags[0]`** — das ist oft Müll (`"Ethiopia"`,
`"Hide From New"`, `"exchange"`), nicht die Kategorie; (b) die Tag-**Reihenfolge** ist unzuverlässig
(bei „Next PM of Ethiopia" steht der kanonische Tag `Politics` **zuletzt**).
### F1. Mapper: kanonischen Tag zuerst, dann Heuristik
- [ ] In `MarketCategoryMapper.Map`: **zuerst** prüfen, ob **irgendein Tag exakt** einer bekannten
Kategorie entspricht (Polymarkets Tag-Vokabular enthält fast immer den kanonischen Top-Level-Tag:
`Sports`, `Politics`, `Crypto`, `Business`/`Economy`, `Pop Culture`, `Science`, ...). Mapping-Tabelle
Tag→`MarketCategory` (inkl. Synonyme: `Finance`/`Business`→Economy, `Pop Culture`→PopCulture).
Erst wenn **kein** kanonischer Tag matcht, die bestehende Keyword-Heuristik auf Frage+Tags anwenden.
- [ ] **Kategorie über die gesamte Tag-Menge** bestimmen, nie über `tags[0]`.
### F2. Subcategory: Noise filtern, sinnvoll wählen, leer normalisieren
- [ ] Organisations-/Müll-Tags herausfiltern (Blacklist: `Hide From New`, `Tournament Futures`,
`Main Election`, `Recurring`, Jahres-Tags wie `2025 Predictions`, `2026 FIFA World Cup`→ok als Sub?, …).
- [ ] Subcategory = spezifischster **verbleibender** Tag, der **nicht** die Kategorie selbst ist
(bei World-Cup-Tags → `Soccer`, nicht `Sports`). Kein passender → leerer String.
- [ ] **NULL/`""` einheitlich als `""`** speichern (behebt die doppelten „Sports/-"-Zeilen: heute
entstehen zwei Gruppen-Keys aus NULL vs. "").
### F3. On-Demand-Markt: Tags nachladen statt `Other` zu speichern
- [ ] In `GetMarketAsync`: wenn `parentTags` leer ist, aber ein Event mit Id vorhanden ist →
**`/events?id=<eventId>` nachladen** (liefert Tags, 1 Extra-Call pro neuem Markt, cachebar) und die
Tags fürs Mapping verwenden. Über den `IRateLimiter` drosseln.
- [ ] Alternativ/zusätzlich: existiert das Parent-Event bereits in unserer DB (aus dem Events-Sync,
`Event.Tags` gefüllt) → **Tags von dort erben**, ganz ohne API-Call.
### F4. Kategorie aus Event-Tags ableiten + Offline-Backfill (der große Hebel)
- [ ] Markt-Kategorie primär aus den **Event-Tags** (`market.Event.Tags`) ableiten, nicht aus den
(leeren) Markt-Feldern. On-Demand-Märkte erben so die Kategorie ihres Events.
- [ ] **Einmaliger Offline-Backfill** (keine API-Calls!): über alle Märkte iterieren, deren Event
Tags hat, und Kategorie/Subcategory aus `Event.Tags` neu ableiten (mit F1/F2). Als Methode in
`RunRecalculateAllTradersAsync` einhängen ODER eigener Dev-Endpoint. Danach `TraderCategoryPerformance`
neu rechnen (passiert durch die ohnehin folgende Trader-Neuberechnung).
### F5. Coverage geschlossener Märkte
- [ ] Sicherstellen, dass der Events-Sync **geschlossene** Events (mit Tags) ausreichend abdeckt —
mindestens für Märkte, die getrackte Trader gehandelt haben (gezielter „fehlende Event-Tags
nachladen"-Schritt in der Reconciliation).
### F6. Tests
- [ ] Mapper: kanonischer Tag gewinnt über Reihenfolge (`"Ethiopia, Elections, ..., Politics"` → Politics;
`"Sports, Soccer, ..."` → Sports, Subcategory `Soccer`).
- [ ] Subcategory: Müll-Tags werden gefiltert; NULL und `""` erzeugen denselben Gruppen-Key
(kein Duplikat mehr).
- [ ] Event-Tag-Vererbung: Markt ohne eigene Tags, Event mit `['Crypto',...]` → Markt wird Crypto.
- [ ] Backfill: Markt in DB als `Other`, Event.Tags = `['Politics',...]` → nach Backfill Politics,
ohne API-Call.
### F7. Reihenfolge
F1+F2 (reiner Mapper, sofort, testbar) → F4-Backfill (heilt Bestand offline) → F3 (On-Demand-Tags für
neue Märkte) → F5 (Coverage). F1/F2/F4 bringen den Großteil, ohne nennenswerte API-Last.