Der Kommentar auf dem Branch fix-event-description-truncation hielt fest, dass
die Methode mit den HasMaxLength-Aufrufen in AppDbContext synchron bleiben muss.
Genau das ist die Falle, die den Bug ausgeloest hat - der Hinweis gehoert an die
Methode. Damit ist der Branch auch inhaltlich restlos aufgegangen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
Die Plandokumente waren durchweg veraltet: 85 von 91 Punkten im
UMSETZUNGSPLAN standen auf offen, obwohl der Code sie enthielt, und in
FIXPLAN-UI-Ranglisten war keine einzige der 18 Aufgaben abgehakt, obwohl
beide zugehoerigen Commits laengst im Zweig stecken. Alles abschnittsweise
gegen den Code geprueft und die Haekchen gesetzt - mit Belegstellen, damit
die naechste Pruefung nicht wieder bei null anfaengt.
Neu: STATUS.md als Einstiegsseite - wo das Projekt steht, was fertig ist,
was offen ist, und was beim Aufraeumen bewusst stehengeblieben ist. CLAUDE.md
verweist darauf.
Nachgezogen:
* UMSETZUNGSPLAN.md - 66 Punkte abgehakt. Offen bleiben A4 (Strategie-
Klassifikation), B2 (Secrets) sowie C3/D1/D2 (SQL-Arbeiten des Nutzers).
* FIXPLAN-UI-Ranglisten.md - abgeschlossen bis auf den als "Optional"
markierten Pfeilrichtungs-Punkt.
* FIXPLAN-TODO.md - Teil D und F abgehakt; die Test-Baseline "16 gruen"
auf die heutigen 126 korrigiert. Offen: F5 und zwei Tests aus F6.
* FIXPLAN-G-Speicher.md - G1 bis G4 als erledigt vermerkt, Baseline "39/1"
korrigiert, Pfad auf die nicht mehr existierende WinFormsHost/appsettings.json
richtiggestellt.
* docs/PLAN-Linux-Portierung.md - der Watchdog-Warnhinweis war ueberholt
(DcHeartbeatService meldet an /api/watchdog/v1/ping). Abschnitt 11 empfahl
noch, mit Phase 0 zu beginnen; jetzt benennt er Phase 5 und 6 als das, was
wirklich aussteht. Die Randnotiz zu Zugangsdaten in LicenseGuard.cs ist
gegenstandslos, die Datei laeuft ueber den Deploymentcenter.Client.
Zwei Befunde, die keine Aufraeumarbeit sind und deshalb nur dokumentiert
wurden - beide in STATUS.md Abschnitt 3:
1. In src/Predictalytics.Api/appsettings.json steht ein echter
OpenRouter-API-Key im Klartext, versioniert seit 7045002. Nicht
eigenmaechtig entfernt: ohne Rotation beim Anbieter bringt das nichts
(die History behaelt ihn), wuerde aber die KI-Analyse abschalten.
Der Schluessel muss zurueckgezogen und neu ausgestellt werden.
2. Die Portierung ist auf keinem Linux-System je ausgefuehrt worden. Die
Verifikation vom 2026-08-08 lief unter Windows.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Toter Code:
* ApiConfiguration.ConfigureApi (29 Z.) hatte keinen Aufrufer mehr.
PredictalyticsHost baut die WebApplication seit Phase 2 selbst auf
(Kestrel, CORS, Swagger, Static Files); ConfigureApi war der
zurueckgebliebene Zwilling aus der Zeit davor.
* IAnalyticsService.GetTraitsAsync samt Implementierung. Die Methode gab
konstant eine leere Liste zurueck; ihr eigener Kommentar hielt fest, dass
sie ungenutzt ist. Der Endpunkt /api/traders/traits liest direkt aus dem
DbContext und bleibt unveraendert.
Ungenutzte Paketreferenzen:
* Swashbuckle.AspNetCore aus Predictalytics.Api - wurde nur von ConfigureApi
gebraucht; Swagger baut Hosting auf, das die Referenz selbst haelt.
* Microsoft.EntityFrameworkCore.Design aus Predictalytics.Worker - die
Design-Time-Factory liegt in Infrastructure.
* Serilog.Sinks.File/.Console und Serilog.Formatting.Compact aus
Predictalytics.Infrastructure - dort wird kein Logger konfiguriert, nur
Serilog.Core/Events/Context verwendet. Die Sinks haengen am Hosting.
Altlast-Dateien:
* Spike/ - Projektdatei ohne eine einzige Quelldatei, net8.0, nicht in der
Solution.
* query.csx - Ad-hoc-Abfrageskript von Juli mit fest eingetragenen
DB-Zugangsdaten.
* NewDesign.zip (388 KB) und temp_new_design/ - Rohmaterial des
Design-Entwurfs vom 15.07. Das Ergebnis liegt fertig in
wwwroot/landing.html und wwwroot/docs.html.
Veraltete Verweise auf den entfernten WinForms-Host:
* CLAUDE.md nannte fuer die Release-Version eine csproj, die es nicht mehr
gibt - sie steht seit der Zentralisierung in Directory.Build.props.
* docs/BETRIEB-Deploymentcenter.md: Anbindung und BuildInfo sitzen in
Predictalytics.Hosting.
* docs/API.md: Die Aussage "ohne Authentifizierung" galt vor Phase 4. Jetzt
mit Header X-Predictalytics-Key und den heutigen Methodennamen.
* Kommentare in Directory.Build.props, DevEndpoints.cs, PredictalyticsHost.cs.
Build ohne neue Warnungen (die 8 bestehenden CS86xx sind unveraendert),
126 Tests gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lagen unfertig im Worktree bzw. auf einem eigenen Branch und werden hier
gebuendelt, damit der Ausgangsstand vor der Aufraeumaktion festgehalten ist.
1. Event-Upsert kappte nur Slug und Title. Description, ImageUrl und Tags
haben in AppDbContext ebenfalls ein HasMaxLength, wurden aber ungekuerzt
geschrieben. Ein Polymarket-Event mit einer Beschreibung ueber 4096
Zeichen liess MySQL den gesamten Batch mit "Data too long for column
'Description'" ablehnen - damit brach der komplette Marktsync ab, nicht
nur dieses eine Event. Ersetzt durch TruncateEventStrings, analog zum
bereits vorhandenen TruncateMarketStrings. Regressionstest deckt beide
Zweige ab (neues und bestehendes Event); uebernommen vom Branch
fix-event-description-truncation, der damit aufgeht.
2. FingerprintSnapshotService warf bei jedem Trader, der in zwei
Unterkategorien derselben Kategorie gehandelt hat. Der eindeutige Index
ist TraderId+Category+Subcategory, ein ToDictionary auf die Kategorie
allein kollidiert deshalb. Jetzt per GroupBy summiert.
3. DcErrorReporter meldete Zustandswechsel nur auf Debug-Ebene. Ein stumm
abgeschalteter oder abgewiesener Fehler-Stream fiel erst auf, wenn im
Deployment Center nach einem Absturz nichts ankam. Meldet jetzt auf
Warning - das erreicht den DcErrorSink nicht (Error+) und erzeugt daher
keine Rueckkopplung. Transportfehler werden nur beim ersten Mal
gemeldet, damit ein Netzwerkaussetzer keine Warnung pro Fehler ausloest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seit Deploymentcenter 2.4 liegt die Release-Ablage hinter HTTP-Basic-Auth mit
dem Lizenzschluessel als Zugangsdatum. Ohne ihn antwortet jeder Paketabruf mit
401. Predictalytics hat noch kein Release veroeffentlicht; nach UPGRADE.md
16.1 entsteht der Schutz fuer ein Produkt mit dem ersten Upload, der erste
ausgelieferte Build muss die Zugangsdaten also bereits mitbringen.
- DcUpdateService reicht licenseKey und platform (PlatformId.Current) an
CheckForUpdateAsync und LaunchUpdateAgent weiter. Fehlt der Schluessel am
Aufruf, greift LicenseClient.TryGetCachedKey - damit ist der Headless-Pfad
mit abgedeckt.
- LaunchAgent startet ohne Lizenzschluessel gar nicht erst. Sonst schliesst
sich die Anwendung, der Agent laeuft in einen 401, und zurueck bleibt eine
geschlossene App ohne Update.
- restartPath und waitForCurrentProcess werden gesetzt: der Agent wartet auf
das Ende dieses Prozesses, statt auf noch gesperrte Assemblies zu schreiben,
und startet die Anwendung danach wieder. Ein null-restartPath ist dafuer
nicht brauchbar - das SDK ersetzt ihn durch denselben ProcessPath -, beim
Start ueber "dotnet App.dll" wird deshalb der Apphost aufgeloest.
- result.Unauthorized wird vor dem "ist aktuell"-Zweig behandelt. Bei 401
setzt das SDK weder Error noch UpdateAvailable; die Pruefung meldete bisher
"aktuell", waehrend in Wirklichkeit keine Updates mehr ankamen.
- Version zentral in Directory.Build.props, damit Hosting und Shell nicht
auseinanderlaufen.
- run.sh setzt unter Linux das beim Entpacken verlorene Ausfuehrungsbit.
.gitattributes haelt Shell-Skripte auf LF: mit core.autocrlf=true traegt die
Arbeitskopie sonst CRLF, und CopyToPublishDirectory nimmt genau die ins
Linux-Paket - dort scheitert der Start an "bad interpreter".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der in Phase 4 offen gebliebene Ende-zu-Ende-Nachweis ist nachgeholt. Geprueft
mit einem Wegwerf-Host, der ausschliesslich den Webserver startet - die Worker
blieben aus, weil parallel eine aeltere Instanz auf derselben Datenbank laeuft.
Zusaetzlich mit ApiSettings__ReadOnlyDatabase=true, sodass weder Migrationen
noch Seeding liefen.
Als externe Bindung diente 127.0.0.2: gilt der Pruefung als oeffentlich,
bleibt aber Loopback, oeffnet also nichts im Netz.
Ergebnis:
- localhost ohne Token: Steuerendpunkte 200, Swagger 200
- extern ohne Token: Steuerendpunkte 404, Swagger 404, capabilities meldet
canControl false
- extern mit Token: ohne Header 401, falscher Token 401, richtiger 200,
Swagger 404
Nebenbei verifiziert: Datenbankverbindung steht (37.803 MB), Web-UI und
Assets werden ausgeliefert, /api/dashboard liefert echte Daten
(151.094 Trader, 38.187.946 Trades).
Nur Dokumentation - keine Codeaenderung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die schreibenden API-Endpunkte waren ungeschuetzt. /api/capabilities meldete
zwar CanControl und AuthRequired aus der Konfiguration, erzwungen wurde davon
nichts: MapPredictalyticsControlEndpoints mappte Jobs, Trader-Steuerung und
Dev-Endpunkte bedingungslos.
Neu entscheidet die Bind-Adresse:
- localhost ohne Token: aktiv wie bisher, das Betriebssystem schuetzt
- localhost mit Token: aktiv, Token wird verlangt
- extern ohne Token: Endpunkte werden gar nicht gemappt, dazu ein Log.Error
- extern mit Token: Endpunkte verlangen X-Predictalytics-Key
Bewusst fail-safe herum, damit eine unbedachte Umstellung der Bind-Adresse
nicht stillschweigend die Steuerschnittstelle oeffnet. ApiTokenFilter
vergleicht laufzeitkonstant ueber CryptographicOperations.FixedTimeEquals.
Swagger ist bei externer Bindung abgeschaltet. /api/capabilities meldet jetzt
den tatsaechlichen Zustand statt einer Konfigurationsabsicht.
Weiter:
- RuntimeSetup.UseInvariantCulture als Erstes in beiden Startpfaden, damit
die Systemlocale nicht auf Zahlen und Zeitstempel durchschlaegt
- Logverzeichnis neben den Einstellungen statt neben der Programmdatei,
ueber PREDICTALYTICS_LOG_DIR uebersteuerbar
- CORS erlaubt immer die eigene Bind-URL, zusaetzliche Herkuenfte weiter
ueber appsettings.json
- fehlendes Schwester-Repo Deploymentcenter scheitert mit verstaendlicher
Meldung samt Klon-URL statt mit einem Fehler ueber eine fehlende csproj
- EgressPoolService: Linux-Verhalten von Socket.Bind kommentiert
24 neue Tests: IsPubliclyBound fuer Loopback-Schreibweisen, Leerwerte und
externe Adressen; dass leere Eingaben bestehende DB-Zugangsdaten nicht
ueberschreiben; und der Token-Filter gegen fehlende, falsche, zu kurze,
zu lange und abweichend geschriebene Token.
Build: 0 Fehler, 8 Warnungen (alle vorbestehend).
Tests: 124 bestanden, 0 Fehler, 1 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge von main: die dort entstandene Deployment-Center-Integration (Lizenz,
Heartbeat mit DB-Health-Check, Fehler-Stream, UpdateService) ist jetzt Teil
des plattformneutralen Kerns. Predictalytics.WinFormsHost ist entfernt.
Nach Predictalytics.Hosting gezogen:
- DcConfig, DcApiClient, DcErrorSink, DcHeartbeatService, DcUpdateService
unveraendert - sie waren bereits plattformneutral
- DcErrorReporter ohne Application.ThreadException und MessageBox; der
UI-Thread-Handler liegt jetzt beim Host und ruft ReportUiThreadException
- LicenseGuard/LicenseSession ohne Dialog und ohne WinForms-Timer. Neu:
TryUseCachedAsync, ActivateAsync, StartPeriodicRevalidation ueber
PeriodicTimer. Die Unterscheidung transienter Fehler und die Warnung vor
ablaufender Gnadenfrist sind unveraendert uebernommen.
- Dc-Einstellungen von AppSettings nach PredictalyticsOptions; die
Watchdog-Einstellungen entfallen
- DcErrorSink im LoggingSetup, Startbanner nutzt DcConfig.AppVersion
BuildInfo.targets wird jetzt von Predictalytics.Hosting importiert.
In der Avalonia-Shell nachgezogen:
- Menue Deployment Center mit Update-Suche und Lizenzstatus
- Einstellungsgruppe Deployment Center statt Watchdog, Update-Kanal als
ComboBox, Server-URL nur zur Anzeige
- Heartbeat-Snapshot mit SELECT-1-Probe wie in der WinForms-Fassung
- Update-Pruefung still beim Start und interaktiv ueber das Menue, mit
NotifyStopping vor dem Start des Update-Agenten
- Lizenzfenster wertet IsTransient aus: bei fehlender Serververbindung wird
nicht behauptet, die Lizenz sei ungueltig
- TextBox.Watermark auf PlaceholderText (in Avalonia 12 veraltet)
Build: 0 Fehler, 8 Warnungen (alle vorbestehend).
Tests: 100 bestanden, 0 Fehler, 1 uebersprungen.
Verifiziert: --license-status meldet gueltig samt Gnadenfrist; die GUI
startet durch, prueft die Lizenz und laeuft gegen den UpdateService.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Loest die getrennten Server Watchdog (watchdog.mhdf.de) und LicenseLabrador
(license.mhdf.de) durch das Deployment Center (dc.mhdf.de) ab.
- DcConfig: einkompilierte Basis-URL und Produkt-Slug, Version aus BuildInfo
- DcApiClient: gemeinsamer HTTP-Zugang
- DcHeartbeatService: Heartbeat mit Metriken und DB-Health-Check
- DcErrorReporter/DcErrorSink: Error- und Fatal-Meldungen an den Fehler-Stream
- DcUpdateService: Update-Pruefung gegen den UpdateService
- LicenseGuard/LicenseDialog: Lizenzgate ueber /api/license/v1/validate,
mit LicenseSession, Hardware-ID v2 und Unterscheidung transienter Fehler
WatchdogHeartbeatService entfernt, Betriebsdoku ersetzt.
Build: 0 Fehler.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Predictalytics hing noch am abgeloesten LicenseLabrador (license.mhdf.de),
waehrend die Lizenzen inzwischen im Deploymentcenter (dc.mhdf.de) gepflegt
werden. Vor dem Umbau mit einem Wegwerf-Probelauf gegen den echten Client
abgesichert: Slug predictalytics, HWID v2 aus machine-guid, Server meldet valid.
Hosting/LicenseGuard.cs auf Deploymentcenter.Client umgeschrieben. Die
Aufteilung bleibt: plattformneutrale Pruefung im Hosting, interaktive
Aktivierung in den Hosts.
Gewinn fuer die Linux-Portierung:
- Hardware-ID v2 mit Quellen-Kaskade /etc/machine-id, dbus, DMI-UUID, MAC,
Schluesseldatei; Container ueber DEPLOYMENTCENTER_HWID
- Der Rechnername steckt nicht mehr im Hash, Umbenennen verbraucht keinen
Aktivierungsplatz
- Cache mit AES-256-GCM statt DPAPI, ohne stillen Klartext-Rueckfall unter
Linux. Damit erledigt sich Punkt 3.2 Nr. 2 der Portierungsanalyse.
- XDG_CONFIG_HOME wird beruecksichtigt
- Keine Basic-Auth-Zugangsdaten mehr im Quelltext
Neue CLI-Schalter aus dem Integrationsleitfaden, ausgewertet vor jeder
Avalonia-Initialisierung: --license-status, --license-set-key,
--license-deactivate. Schluessel werden in der Ausgabe maskiert.
Die Lizenzfenster beider Hosts zeigen jetzt die Hardware-ID an, an die der
Server die Aktivierung bindet.
Build: 0 Fehler. Tests: 100 bestanden.
Verifiziert: --license-status meldet gueltig; die GUI startet vollstaendig
durch bis MainWindowViewModel.Start(), belegt durch die Logdateien.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neues Projekt Predictalytics.Shell (Avalonia 12.1.1, net10.0) als
plattformuebergreifender Ersatz fuer den WinForms-Host. Setzt auf dem in
Phase 2 extrahierten Predictalytics.Hosting auf.
Oberflaeche:
- MainWindow mit Menue, Werkzeugleiste, Terminal-Tab, Einstellungen-Tab und
Statusleiste. Die feste Fenstergroesse von 1886x1088 ist aufgeloest.
- Terminal als ListBox ueber ObservableCollection mit Einfaerbung nach
Loglevel. Ringpuffer statt des bisherigen kompletten Leerens bei 500 Zeilen.
- Einstellungsansicht handgeschrieben als Ersatz fuer den PropertyGrid, den
es in Avalonia nicht gibt: vier Gruppen, Passwortfelder, mehrzeiliges
Textfeld fuer die Egress-Kanaele, ComboBox fuer den SSL-Modus. Gespeichert
wird explizit statt bei jeder Einzeloperation.
- LicenseWindow ersetzt den WinForms-LicenseDialog.
- Dialogs.cs als schlanker Ersatz fuer MessageBox.Show, ohne Drittanbieter.
Headless-Modus (--headless) im selben Binary: fuer Linux-Server ohne
Desktop-Session. Die Argumentauswertung steht vor jeder Avalonia-
Initialisierung, sonst stirbt der Prozess ohne X11 bevor der Schalter greift.
Lizenz ueber PREDICTALYTICS_LICENSE_KEY statt Dialog, SIGTERM und SIGINT
ueber PosixSignalRegistration fuer sauberes systemd-Stop.
Exit-Codes: 0 planmaessig, 2 keine nutzbare Lizenz, 3 Fehler.
Plattformdetails: Schrift-Fallbackkette fuers Terminal, Avalonia.Fonts.Inter
als mitgelieferte UI-Schrift, UTF-8 fuer die Windows-Konsole im
Headless-Modus.
Die Avalonia-Basisklasse wird als global::Avalonia.Application angegeben:
innerhalb von Predictalytics.* loest der kurze Name auf den eigenen
Namespace Predictalytics.Application auf.
Build: 0 Fehler, 0 Warnungen. Tests: 100 bestanden.
Verifiziert: --headless laeuft bis zur Lizenzschranke und beendet sich mit
Exit-Code 2; GUI startet und zeigt das Lizenzfenster. Das Hauptfenster
konnte nicht geprueft werden, weil die hinterlegte Lizenz abgelaufen ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Neues Projekt Predictalytics.Hosting nimmt auf, was bisher im
windows-gebundenen WinFormsHost feststeckte, aber portabel ist:
- PredictalyticsHost (aus EmbeddedWebServer): Kestrel- und Worker-Lifecycle,
Wartungsaktionen, DB-Groesse. Meldet Zustandswechsel ueber StateChanged.
- PredictalyticsOptions (aus AppSettings): ohne WinForms-Bezug. Die
System.ComponentModel-Attribute sind plattformneutral und bleiben, damit
das PropertyGrid Gruppen und Beschreibungen behaelt.
- LoggingSetup (aus Program.cs): Serilog-Aufbau, Terminal-Sink als optionale
Action statt fester RichTextBox.
- LicenseGuard: GUI-frei. Periodische Revalidierung ueber PeriodicTimer statt
WinForms-Timer, Abbruch ueber Callback statt Application.Exit. Der
interaktive Dialogaufruf bleibt als LicenseGate im WinForms-Host.
- WatchdogHeartbeatService unveraendert verschoben.
Infrastructure: RichTextBoxSink -> DelegateSink umbenannt (war nie
WinForms-abhaengig, nur missverstaendlich benannt).
Einstellungen liegen jetzt unter %APPDATA%/Predictalytics bzw.
~/.config/Predictalytics statt neben der Programmdatei, mit einmaliger
Uebernahme aus dem alten Ort. Das Installationsverzeichnis ist unter Linux
ueblicherweise nicht beschreibbar.
wwwroot wird ueber einen Content-Eintrag neben die Programmdatei kopiert;
die frueheren Pfad-Heuristiken entfallen.
Hosting und WinFormsHost nutzen Microsoft.NET.Sdk statt Sdk.Web: der Web-SDK
globbt wwwroot automatisch als Static Web Asset und kollidiert mit dem
Content-Eintrag. WebApplication kommt ueber FrameworkReference.
Neu konfigurierbar (verhaltensgleiche Defaults): WebserverHost fuer die
Kestrel-Bind-Adresse, DbSslMode fuer die MySQL-Verschluesselung.
explorer.exe-Aufrufe durch ProcessStartInfo mit UseShellExecute ersetzt —
funktioniert unter Windows und Linux.
Build: 0 Fehler. Tests: 100 bestanden, 0 Fehler, 1 uebersprungen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zielframework aller Projekte auf net10.0 (WinFormsHost: net10.0-windows),
zentral in Directory.Build.props statt je csproj. SatelliteResourceLanguages
ebenfalls zentralisiert.
Neue Directory.Packages.props mit ManagePackageVersionsCentrally: 21 Pakete,
Versionsangaben aus allen csproj entfernt.
EF Core bleibt bewusst auf 9.0.18 mit Pomelo 9.0.0. Pomelo hat keine
EF-Core-10-Version; 9.0.0 pinnt EntityFrameworkCore.Relational auf
[9.0.0, 9.0.999]. Die EF-9-Pakete zielen auf net8.0 und laufen auf der
net10.0-Runtime. Begruendung als Kommentar in Directory.Packages.props.
Uebrige Pakete auf aktuellen Stand: Swashbuckle 10.2.3 (ohne Bruchstellen),
Serilog 4.4.0, Microsoft.Extensions 10.0.10, Test-SDK 18.8.1, coverlet 10.0.1.
Sicherheits-Pin: SQLitePCLRaw 2.1.12 statt transitiv 2.1.10
(GHSA-2m69-gcr7-jv3q, High). Dazu CentralPackageTransitivePinningEnabled.
Build: 0 Fehler, 8 Warnungen (alle vorbestehend).
Tests: 100 bestanden, 0 Fehler, 1 uebersprungen, Testhost net10.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vorarbeiten fuer die Linux-/NET-10-Portierung (siehe docs/PLAN-Linux-Portierung.md).
DateTime.TryParse ohne DateTimeStyles liefert bei Z-Zeitstempeln Kind=Local und
rechnet in Lokalzeit um. An 13 Stellen in PolymarketProvider und LimitlessProvider
wurden so ISO-Zeitstempel der Plattform-APIs eingelesen, waehrend der Fallback
derselben Zuweisungen (DateTime.UtcNow) korrektes UTC schrieb - die Spalten
enthielten also gemischt verschobene und korrekte Werte. Auf einem Server mit
TZ=UTC haette derselbe Code andere Werte erzeugt als unter Windows.
Neuer Helfer Infrastructure/Helpers/DateParsing.cs kapselt das Parsen mit
InvariantCulture und AdjustToUniversal|AssumeUniversal; alle Fundstellen umgestellt.
Ausserdem:
- LimitlessProvider: Volume-Parsing auf InvariantCulture umgestellt
- EmbeddedWebServer: hartkodierten j:\-Absolutpfad als wwwroot-Fallback entfernt
Ein Backfill der Altdaten erfolgt bewusst nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
license.mhdf.de leitet inzwischen http->https um. .NET wandelt beim
Folgen eines Redirects ein POST in ein GET um; der Server antwortet auf
GET /validate mit 405. Das SDK wertet jeden Nicht-2xx-Status als
"Server unerreichbar", faellt in den Offline-Fallback und meldet
NoLicense statt des echten Grundes - die Aktivierung war damit auf
frischen Installationen ohne Cache unmoeglich.
Nebeneffekt der Korrektur: die BasicAuth-Credentials gingen bisher im
Klartext ueber die Leitung. DEPLOYMENT.md fordert ohnehin TLS.
Verifiziert mit geleertem Cache: Erstaktivierung und Revalidierung
liefern beide Valid.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#1 increment 2: cluster the co-movement graph + network visualization
Completes the smart-money discovery lever beyond the per-seed list:
- Pure CoMovementGraphBuilder (Application): links wallets by shared timely
co-entries and clusters the graph via union-find (connected components).
- GET /api/co-movement/graph builds the graph over copy-relevant + insider
wallets and weights each node by an "informed share" — how often the wallet
entered before a big favorable price move (from stored price snapshots,
graceful when absent). This is the "co-move before price moves" signal.
- UI: a new "Netzwerk" page rendering an SVG cluster graph (node size = score,
color = cluster, gold ring = insider, green ring = informed leader);
click a node opens the trader.
- Tests: min-edge-weight, window filtering, connected-component clustering.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
#4 Copy-portfolio: diversified master mix instead of single-score ranking
The leaderboard ranks single scores, but the top N can be three correlated
weather bots. Suggest a de-clumped set instead:
- Pure CopyPortfolioBuilder (Application): greedily picks high-scoring masters
while enforcing a per-category cap and a max pairwise correlation; reports who
was dropped for correlation vs the category cap.
- GET /api/portfolio/suggest?size=8&maxPerCategory=2&maxSimilarity=0.6 builds the
position-overlap similarity (signed market sets) among copy-relevant masters
and runs the diversifier. New PortfolioEndpoints group.
- UI: a "Copy-Portfolio" nav page — the diversified picks with category, score,
copyability, PnL and the pick reason, plus a funnel summary (candidates /
dropped for correlation / dropped for category cap).
- Tests: top-score order, category cap, correlation drop, size limit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
- Shutdown-Event sendet stopped_graceful statt stopping: der Server
kennt "stopping" im Router, aber event_log.kind ist ein ENUM ohne
diesen Wert. Folge war HTTP 500 nach dem Zustandswechsel, das Event
fehlte in der Historie.
- Default-Source auf "Predictalytics" korrigiert (Schreibweise des
bereits angelegten Monitors auf dem Server).
Heartbeat, Metrics-Payload, Lizenzvalidierung und der Startpfad ueber
den Cache sind end-to-end gegen die Produktivserver getestet.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bindet die beiden neuen Betriebsprojekte an:
- WatchdogHeartbeatService: periodischer POST /api/heartbeat an
watchdog.mhdf.de (Dead-Man's-Switch), stopping-Event beim Beenden,
Konfiguration ueber AppSettings-Kategorie "Watchdog". Fehler sind
best effort und beeintraechtigen die App nie.
- LicenseGuard + LicenseDialog: Lizenzpruefung vor dem Start der
MainForm, 12h-Revalidierung zur Laufzeit, Checksum-Haertung.
Produkt-Slug/Endpoint/Public-Key sind einkompiliert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#1 Smart-money co-movement detection (discovery increment 1)
The correlation endpoint was only pairwise + position-overlap based. Add timing-
based, one-to-many co-movement discovery: for a seed trader, find the wallets
that repeatedly enter the SAME outcomes within a time window — surfacing new
informed traders rather than just ranking known ones.
- Pure CoMovementCalculator (Application): ranks candidate wallets by shared
co-entered markets; positive AvgLeadHours = the wallet tends to move BEFORE the
seed (the informed-trader signal).
- GET /api/traders/{id}/co-movement?windowHours=48&minShared=3 (bounds the seed
to its last 500 buys) + CoMovingWalletDto.
- UI: a co-movement card on the trader detail page listing the top related
wallets with shared-market count and lead/lag (green when they move first).
- Tests: min-shared threshold + window filtering, lead sign, ranking order.
Next increment: cluster the co-movement graph + a visual; weight co-entries that
precede significant price moves. Endpoint is on-demand per seed for now.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
#5 Edge-freshness: is a master edge current or stale?
Compares the recent out-of-sample window (last 60d) against the older one
(day 60-180) — both already produced per run by the analytics worker, so no
worker changes and no schema change.
- Pure EdgeFreshnessCalculator (Application): Fresh / Stable / Fading /
Insufficient + a 0-100 freshness score. Fading when return/market drops past a
threshold or a once-strong profit factor collapses below break-even.
- GET /api/traders/{id}/edge-freshness reads the two windows and computes it.
- UI: a colored edge-freshness badge on the trader detail page
(🟢 frisch / 🟡 stabil / 🔴 verblasst) with the return delta and market counts.
- Tests: 5 scenarios (insufficient, fresh, return collapse, PF collapse, stable).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
#3 Strategy-drift alarm: fire + surface drift off the fingerprint history
Turns the drift foundation into an actual alarm that protects copiers:
- New AlertType.StrategyDrift (severity 3).
- FingerprintSnapshotService.GetDriftedTradersAsync lists every master whose
latest fingerprint drifted from its baseline.
- AlertService.EvaluateStrategyDriftAsync (run from EvaluateAlertsAsync) fires a
StrategyDrift alert per drifted master, summarizing the changed dimensions.
Drift is slow-moving, so alerts are de-duplicated per trader over a 7-day
cooldown via new IAlertRepository.ExistsRecentAsync.
- UI: 📉 icon in the alert feed + a drift banner on the trader detail page
(fetches /fingerprint-drift, lists the drifted dimensions).
- Test: drift alert fires once then dedups within the cooldown.
No schema change (reuses TraderFingerprintSnapshots + Alerts).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
Fingerprint-snapshot foundation + strategy-drift calculator (#3/#5 groundwork)
TraderAnalytics is one row per trader, overwritten every recalculation, so there
is no history to detect strategy drift (#3) or edge fade (#5) against. Add the
missing time series:
- TraderFingerprintSnapshot entity (score, category concentration, conviction,
P50/P90 sizing, hold duration, trades/week, category-mix JSON, trait-set JSON)
+ migration AddFingerprintSnapshots (indexed by TraderId, CapturedAt).
- FingerprintSnapshotService (Infrastructure): CaptureDueAsync snapshots every
copy-relevant trader (CopytradingScore >= 40) at most ~once/day; wired into
ScoringAndAlertsWorker. GetDriftAsync reads latest-vs-baseline drift.
- FingerprintDriftCalculator (pure, Application): flags score drop, concentration
shift, sizing jump, conviction sign-flip, category-mix TVD, trait-set change.
- GET /api/traders/{id}/fingerprint-drift?baselineDays=14 read endpoint.
- Tests: drift calculator (4 scenarios) + capture service (copy-relevance,
throttle, drift read).
This is the shared foundation both #3 (drift alarm) and #5 (edge freshness) build on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
Rework insider feed: system-level InsiderWatch + dedicated view (no watchlist writes)
Watchlists will become per-user once the product is offered commercially, so the
system must not auto-add/remove traders there. Decouple insider tracking entirely:
- New system-owned entity InsiderWatch (TraderId unique, FirstDetectedAt,
LastAlertedTradeAt) + IInsiderWatchRepository; migration AddInsiderWatch.
- AlertService.EvaluateInsiderWatchAsync now maintains InsiderWatch (not the
watchlist): registers each possible_insider wallet, seeds the high-water mark
at detection time, and fires one InsiderActivity alert per new trade. Dedup via
LastAlertedTradeAt.
- Dedicated "Insider" view: GET /api/traders/insiders + InsiderDto + a new
Insider-Radar page (sorted by market-surprise). Read-only, separate from watchlist.
- Revert the WatchlistEntry.LastInsiderAlertAt field + its migration (unapplied);
drop the now-unused IWatchlistRepository.UpdateAsync.
- Tests updated to assert InsiderWatch registry (and that no WatchlistEntry is created).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
Insider-Follow feed: auto-watchlist possible-insider wallets + new-trade alerts
Makes the existing possible_insider trait actionable:
- New AlertType.InsiderActivity (severity 4).
- AlertService.EvaluateInsiderWatchAsync (run from EvaluateAlertsAsync every
15 min): auto-adds every possible_insider wallet to the watchlist, then fires
one InsiderActivity alert per new trade one of them places. These wallets
trade rarely, so a single new trade is the strongest copy signal.
- Dedup + no history spam via WatchlistEntry.LastInsiderAlertAt (migration
AddWatchlistLastInsiderAlertAt); a freshly auto-added wallet is baselined at
AddedAt so backfilled trades never alert.
- Repo support: ITraderRepository.GetByTraitAsync, IWatchlistRepository.UpdateAsync.
- UI: distinct 👁 icon for insider alerts (💰 for large positions).
- Tests: auto-add-without-history-alert, alert-on-new-trade-with-dedup.
Migration auto-applies on startup (DependencyInjection.MigrateAsync).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
UI-U1/U2/U4/U5: dashboard showcases + server-side leaderboard sort
- U1: render the /api/traders/showcases sections on the dashboard as
clickable curated leaderboard cards (red_flags visually accented).
- U2: loadTraders() now sends the sort key to the API (real top-N),
take raised to 200; only name/platform stay client-sorted.
- U4: #tradersSort dropdown extended with calmar/conviction/profitfactor/
pnl30d; currentSort synced before building the query.
- U5: fix empty-state colspan 11 -> 10 (table has 10 columns).
Includes FIXPLAN-UI-Ranglisten.md documenting the plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
UI-U3: server-side min-winrate/min-copyability filters
Add optional minWinRate/minCopyability params to GetTradersAsync so the
leaderboard min-value filters see the full pool instead of being applied
client-side to only the first page of results. Covered by a new
AnalyticsService test (three traders, both filters).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
- GET /api/traders/showcases: curated dashboard sections (copy-ready, smooth
operators, rising stars, high conviction, specialists, insider watch, red
flags), each encoding a selection funnel over the persisted analytics/traits.
Pure ShowcaseBuilder holds the ranking logic (+6 unit tests).
- GET /api/traders?sort=: leaderboard sort keys (pnl, pnl30d, winrate,
copytrading, calmar, conviction, profitfactor) over the loaded set.
Read-only, persisted-data-only (public-tier safe). 76 tests, 1 skip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two deeper analyses, both pure from existing data (no new API cost):
- ConvictionEdgePct: return% of the biggest-bet third minus the smallest-bet
third of closed markets. Positive => sizing carries information (copy
size-weighted); negative => overbets losers (red flag). CalculateMarketWinRates
now emits per-market (invested, returnPct) pairs consumed by
StrategyMetricsCalculator.ComputeConvictionEdge.
- CategoryConcentration: Herfindahl index of category volume shares
(specialist vs generalist), from the category-performance dict.
Stored on TraderAnalytics, exposed on TraderDetailDto. Migration
AddStrategyFingerprintMetrics. +7 unit tests (70 total, 1 skip).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New RiskMetricsCalculator (pure) derives max drawdown (USD), daily-PnL volatility
and longest losing streak from the trader's TraderDailySnapshot equity curve; the
PnL engine computes them each recalc and stores them on TraderAnalytics (+ computed
ReturnOverMaxDrawdown, Calmar-like). Two traders with identical final PnL but a
smoother path are very differently copyable — this captures that. Exposed on
TraderDetailDto. Migration AddRiskAdjustedMetrics. +5 unit tests (63 total, 1 skip).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Aggregated ingest tier bucketed trades by hour incl. the current, still-growing
hour, then upserted via ON DUPLICATE KEY UPDATE (mutable rows). The PnL engine
checkpoints positions by row Id, so a bucket that keeps growing after being applied
had its later growth silently skipped (Id <= LastAppliedTradeId).
Extract the duplicated aggregation logic from PollingWorker + TradeHistoryWorker into
TradeAggregation.AggregateCompletedHours, which only aggregates COMPLETED hours; the
current hour is deferred (re-fetched next cycle) so every persisted aggregate is
immutable. +3 unit tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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
- TraderDetailDto now exposes PnL7d/WinRate7d/PnL24h/WinRate24h and
CurrentBalance — the engine has computed these all along but the API
never delivered them.
- Removed POST /api/dev/repair-db: its raw SQL referenced non-existent
columns/tables (Trades.Type/Payout, Traders.LastPositionsUpdatedAt,
table "Jobs") and would have deleted ALL TraderPositions including
pruned-history conserves. The supported repair path is the WinForms
"Recalculate All Traders" action.
- Swagger tags for Jobs and Dev groups; full endpoint reference in
docs/API.md (kept generic — external consumers like PolyTrader adapt
to our API, not vice versa).
- FIXPLAN Teil E: master-selection gap analysis as generic extensions
(profile endpoint, out-of-sample window metrics, price-band profile
with per-band win rate, stop-loss ratio, copyability aggregates with
category fees, correlation endpoint, martingale trait).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Win rate alone hides the payoff profile: an arb grinder (95% wins at +2%)
and a longshot holder (25% wins at +400%) need entirely different copy
strategies. D2c adds median+avg return of won AND lost closed markets,
profit factor, UI cards, and two derived traits (thin_margin_wins,
high_payoff_wins). Median leads (outlier-robust), the loss side is
mandatory — high win rates with rare total losses (resolution farming)
are exactly what the win rate alone obscures.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Trait filtering is now a binding requirement: trait query parameter on
GET /api/traders (EXISTS-based), GET /api/traits with counts, dropdown
plus clickable chips in the UI.
- New trait possible_insider (D2b): detection via market-surprise
likelihood under market prices (won longshots count, won favorites do
not), with base filters (rare, large stakes, resolution farmers
excluded) and a v2 roadmap (buys-before-news timing, one-shot wallets,
topic concentration, urgency/slippage, niche markets).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- MarketCategoryMapper: classify from question text (the Gamma /markets
endpoint delivers neither category nor event tags, so on-demand markets
had no signal at all), match short tokens on word boundaries ("eth" no
longer hits inside "whether", "pop" not inside "popular"), widen the
keyword lists across all categories.
- UpdateMarketFields: never overwrite a tag-derived category with an
uninformative "Other" from the on-demand path.
- PositionPnLEngine: sync Trader.TotalTrades to the actual replayed row
count — the worker-side increment counters drift (INSERT IGNORE,
deletions, historic imports) and produced Trades30d > TotalTrades.
- Tests: 14 new (mapper classification + word-boundary regression,
TotalTrades sync + Trades30d invariant, category update guard via
SQLite) — suite now 32 green + 1 skip.
- FIXPLAN Teil D for the larger rebuilds (AggregatedCount column,
TraderTraits heuristics, IngestMode tiering for ultra-HF traders).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The reconciliation worker resets position checkpoints via bulk UPDATE while
the analytics worker saves recalculated positions for the same trader —
MySQL then rejects the stale save ("Record has changed since last read").
The engine run is now retried once with a fresh context (re-reading the
concurrent change); if it still collides, it logs a warning and lets the
next cycle pick the trader up again (LastAnalyzedAt stays unset).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Shared ApiConfiguration.MapPredictalyticsEndpoints() used by both the
standalone API and the embedded WinForms Kestrel server: the embedded
host was missing /api/watchlist and /api/dev (empty watchlist page),
the standalone host was missing /api/search.
- New Development menu action "Recalculate All Traders": backfills
ResolutionOutcome from snapped outcome prices, wipes derived analytics
(snapshots, category stats, rebuildable positions), zeroes aggregates
and marks every trader for re-analysis. Raw trades untouched.
- Static files now served with Cache-Control: no-cache in both hosts,
plus ?v= cache-buster on app.js/style.css (stale browser JS masked
earlier UI fixes).
- Watchlist hardened: endpoint null-safe, real Analytics.CopytradingScore,
Platform field; repository includes Trader.Analytics; page shows a
visible error row instead of staying silently blank.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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).
- Design-time AppDbContextFactory now builds its connection string via
MySqlConnectionStringBuilder from PREDICTALYTICS_DB_* env vars instead of a
hardcoded local default, so no secret needs to live in source/config to run
migrations against any target database.
- InitialBaseline migration applied end-to-end against a fresh dev MySQL DB
and confirmed via `dotnet ef migrations list`.
Clean Architecture .NET 8 solution (Domain/Application/Infrastructure/Api/Worker/WinFormsHost)
for analyzing Polymarket traders for copytrading/strategy-replication candidates.
Includes EF Core InitialBaseline migration and DB secrets removed from source/config
in preparation for version control.