Files
Predictalytics/ROADMAP.md
T
RichardandClaude Opus 5 55daa2c9de Roadmap 0.1, 1.2 und 1.3: Key raus, Build warnungsfrei, Backfill eingegrenzt
0.1 OpenRouter-Schluessel aus appsettings.json entfernt. Laut Nutzer war er
bereits inaktiv, eine Rotation beim Anbieter also nicht mehr noetig. Das Feld
bleibt leer stehen, mit Hinweis daneben: der Schluessel gehoert in die
Umgebungsvariable OpenRouter__ApiKey, die ohnehin Vorrang hat.

Damit die Anwendung ohne Schluessel sauber laeuft, steigt OpenRouterApiClient
jetzt frueh aus, statt zu senden. Vorher haette jeder Analyseversuch einen 401
erzeugt - der als Error im Fehler-Stream des Deployment Centers gelandet waere,
worauf der Aufrufer die Fehlermeldung als JSON zu parsen versucht und einen
zweiten Fehler erzeugt haette. Der Zustand wird einmal als Warning gemeldet.
Die Konstante NotConfigured liegt im Interface, weil Application die
Infrastructure-Schicht nicht referenziert.

1.2 Alle acht Nullable-Warnungen behoben, der Build ist jetzt warnungsfrei.
Es waren durchweg Annotationsfragen, kein Verhalten aendert sich:
* TradeRepository (6x) - die Parameterliste nimmt bewusst null auf, damit
  nullable Spalten als SQL-NULL geschrieben werden. Jetzt List<object?>.
* TraderAnalyticsWorker - ThenInclude(o => o!.Market); der Lambda ist ein
  Ausdrucksbaum fuer EF und laeuft nie.
* PolymarketProvider - raw.Question ?? ""; TruncateMarketStrings normalisierte
  den Wert einen Schritt spaeter ohnehin auf "".

1.3 Der Kategorie-Backfill holte den gesamten Marktbestand samt Event in den
Speicher und verwarf den Grossteil sofort wieder. Die Einschraenkung auf
Maerkte mit gefuellten Event-Tags steht jetzt in der Abfrage statt in der
Schleife. Ergebnis unveraendert.

Dabei einen eigenen Fehler in der Roadmap korrigiert: dort stand, dieser
Backfill laufe "bei jedem Start". Das stimmt nicht - er gehoert zu
RunRecalculateAllTradersAsync, der manuellen Wartungsaktion, die nur auf
Knopfdruck laeuft. Die Startzeit war nie betroffen. Die falsche Aussage stand
auch in STATUS.md und ist dort ebenfalls richtiggestellt.

Build warnungsfrei, 126 Tests gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 14:07:53 +02:00

16 KiB
Raw Blame History

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. Die alten Dokumente liegen vollständig unter 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 aus dem Repository entfernt

Erledigt am 2026-08-24. Der Schlüssel war laut Nutzer bereits inaktiv und wurde deshalb ersatzlos aus src/Predictalytics.Api/appsettings.json entfernt — eine Rotation war nicht mehr nötig.

Für den Ersatz: den neuen Schlüssel nicht in die Datei schreiben, sondern als Umgebungsvariable OpenRouter__ApiKey setzen. WebApplication.CreateBuilder liest Umgebungsvariablen mit Vorrang vor appsettings.json, eine Codeänderung braucht es nicht. Die Datei trägt jetzt einen entsprechenden Hinweis neben dem leeren Feld.

Ohne Schlüssel läuft die Anwendung sauber weiter: OpenRouterApiClient sendet gar nicht erst und meldet den Zustand einmalig als Warnung. Vorher hätte jeder Analyseversuch einen 401 erzeugt, der als Fehler im Deployment Center gelandet wäre und dessen Fehlertext der Aufrufer anschließend als JSON zu lesen versucht hätte.

Der alte Schlüssel bleibt im Git-Verlauf (ab Commit 7045002). Das ist hier folgenlos, weil er inaktiv ist — bei einem aktiven Schlüssel wäre die Rotation beim Anbieter der einzige wirksame Schritt gewesen.

Herkunft: 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 Nullable-Warnungen behoben — der Build ist warnungsfrei

Erledigt am 2026-08-24. Alle acht waren Annotationsfragen, keine echten Fehler; das Laufzeitverhalten ist unverändert:

  • TradeRepository.cs (6×) — die Parameterliste nimmt bewusst null auf, damit nullable Spalten als SQL-NULL geschrieben werden. Jetzt als List<object?> deklariert, statt an sechs Stellen zu unterdrücken.
  • TraderAnalyticsWorker.cs:152ThenInclude(o => o!.Market). Der Lambda ist ein Ausdrucksbaum für EF und wird nie ausgeführt.
  • PolymarketProvider.cs:357raw.Question ?? "". Das Feld wurde ohnehin einen Schritt später von TruncateMarketStrings auf "" normalisiert.

Der Build sollte warnungsfrei bleiben. Eine neue Warnung fällt nur auf, solange es keine alten gibt.

1.3 Kategorie-Backfill lädt nicht mehr den ganzen Marktbestand

Erledigt am 2026-08-24. Der Backfill in RunRecalculateAllTradersAsync holte alle Märkte samt Event in den Speicher und verwarf den Großteil sofort wieder. Die Einschränkung auf Märkte mit gefüllten Event-Tags steht jetzt in der Abfrage statt in der Schleife; das Ergebnis ist unverändert.

Korrektur zur ersten Fassung dieser Roadmap: dort stand, das laufe „bei jedem Start". Das war falsch — es ist die manuelle Wartungsaktion Recalculate All Traders, die nur auf Knopfdruck in der Shell läuft. Die Startzeit war nie betroffen, das Speicherrisiko bei der Aktion selbst schon.


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, 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 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 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 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 (Anhang) und 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.

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, ConditionIdTokenId) ä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 01)

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.

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.