Commit Graph
30 Commits
Author SHA1 Message Date
RichardandClaude Opus 5 cb58e094b6 Drei offene Bugfixes sichern (Stand vor dem Fruehjahrsputz)
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>
2026-08-22 10:46:46 +02:00
RichardandClaude Opus 5 b175df6d3e Phase 4: Steuerendpunkte absichern, Kultur und Log-Pfad plattformfest
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>
2026-08-08 21:13:49 +02:00
RichardandClaude Opus 5 c9eff9f75e Phase 1: Solution auf .NET 10 und zentrale Paketverwaltung
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>
2026-08-06 22:54:18 +02:00
Richard be56eadcc5 @
#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>
@
2026-08-03 23:06:36 +02:00
Richard 2eaa67fae2 @
#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>
@
2026-08-03 19:45:35 +02:00
Richard 351e938769 @
#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>
@
2026-07-24 12:27:10 +02:00
Richard e5ce69793b @
#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>
@
2026-07-24 11:44:23 +02:00
Richard b7bee86ed7 @
#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>
@
2026-07-24 09:50:42 +02:00
Richard b21da1c2c7 @
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>
@
2026-07-23 21:21:21 +02:00
Richard 2bec11d0a9 @
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>
@
2026-07-23 20:52:37 +02:00
Richard 8b9b34342f @
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>
@
2026-07-23 19:46:36 +02:00
Richard d8c204995a @
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>
@
2026-07-23 15:56:58 +02:00
RichardandClaude Opus 4.8 02c5a4d6f4 Add showcase leaderboards endpoint + sortable trader list
- 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>
2026-07-23 09:43:11 +02:00
RichardandClaude Opus 4.8 eaccdadddf H2: strategy-fingerprint metrics (conviction/sizing edge + category concentration)
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>
2026-07-23 09:17:58 +02:00
RichardandClaude Opus 4.8 dcac62165e H1: risk-adjusted return metrics (equity-curve smoothness = copyability)
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>
2026-07-20 09:14:15 +02:00
RichardandClaude Opus 4.8 be1b90b556 Fix Aggregated-tier mutable-bucket vs checkpoint bug
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>
2026-07-20 08:57:46 +02:00
Richard 3ed0b4df27 D3: Implement IngestMode classification, weekly biopsy, SnapshotOnly bypass, and Aggregated import grouping 2026-07-19 17:23:19 +02:00
Richard 6fd7e563f4 G3 & G4: Map UsdcSize, OutcomeIndex, compute Category ROI and add migration 2026-07-19 12:35:03 +02:00
Richard 8a39b912a6 G1 & G2: Storage Governor and Aggregated retention pruning 2026-07-19 10:56:12 +02:00
Richard 7045002ca3 WebUI Redesign and Component 1: category mapper fixes 2026-07-19 10:51:07 +02:00
Richard 16431f38a5 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
2026-07-14 09:04:31 +02:00
RichardandClaude Fable 5 d2f3ec2bd0 Fix category mapping and TotalTrades drift, plan traits/tiering (Teil D)
- 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>
2026-07-11 14:09:12 +02:00
Richard 1787422243 Fix: Implement FixPlan part A9, A10, B 2026-07-10 12:04:27 +02:00
Richard 44f48284a2 Fix 10 critical bugs in Engine, Retention, Reconciliation and API 2026-07-09 19:37:02 +02:00
Richard 21da1e867f Add Fable's Stufe A invariant tests 2026-07-09 18:42:31 +02:00
Richard 882226c6c6 Add unit tests for Engine and Scoring fixes 2026-07-07 18:38:23 +02:00
Richard dc0b67deaa test: fix AnalyticsServiceTests by mocking missing dependencies and adding moq 2026-07-05 19:46:21 +02:00
Richard e9e9ce0d5a feat: Add category performance, update WebUI and OpenRouter integration, fix bugs 2026-07-05 14:13:16 +02:00
Richard d102af2965 Enhance UI, add AI integration, improve logging and database stats 2026-07-04 21:11:31 +02:00
Richard 7a44914d9d Implement B4: Add Predictalytics.Application.Tests project with unit tests for PositionPnLEngine and AnalyticsService 2026-07-03 11:22:43 +02:00