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>
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>
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>
#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>
@
#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-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
- 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>