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
This commit is contained in:
Richard
2026-07-14 09:04:31 +02:00
parent a1fcb4ace5
commit 16431f38a5
39 changed files with 9028 additions and 167 deletions
@@ -22,6 +22,8 @@ public class AppDbContext : DbContext
public DbSet<TraderCategoryPerformance> TraderCategoryPerformances => Set<TraderCategoryPerformance>();
public DbSet<TradeContext> TradeContexts => Set<TradeContext>();
public DbSet<BackgroundJob> BackgroundJobs => Set<BackgroundJob>();
public DbSet<TraderTrait> TraderTraits => Set<TraderTrait>();
public DbSet<TraderWindowMetrics> TraderWindowMetrics => Set<TraderWindowMetrics>();
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
@@ -40,6 +42,30 @@ public class AppDbContext : DbContext
.HasForeignKey<TraderScore>(s => s.TraderId).OnDelete(DeleteBehavior.Cascade);
});
// TraderTrait
mb.Entity<TraderTrait>(e =>
{
e.HasKey(t => t.Id);
e.HasIndex(t => new { t.TraderId, t.Trait }).IsUnique();
e.Property(t => t.Value).HasPrecision(18, 4);
e.HasOne(t => t.Trader).WithMany(tr => tr.Traits)
.HasForeignKey(t => t.TraderId).OnDelete(DeleteBehavior.Cascade);
});
// TraderWindowMetrics
mb.Entity<TraderWindowMetrics>(e =>
{
e.HasKey(t => t.Id);
e.HasIndex(t => new { t.TraderId, t.WindowStart, t.WindowEnd }).IsUnique();
e.Property(t => t.WinRate).HasPrecision(8, 4);
e.Property(t => t.AvgReturnPct).HasPrecision(18, 4);
e.Property(t => t.MedianWinReturnPct).HasPrecision(18, 4);
e.Property(t => t.MedianLossReturnPct).HasPrecision(18, 4);
e.Property(t => t.ProfitFactor).HasPrecision(18, 4);
e.HasOne(t => t.Trader).WithMany()
.HasForeignKey(t => t.TraderId).OnDelete(DeleteBehavior.Cascade);
});
// Trade
mb.Entity<Trade>(e =>
{
@@ -193,6 +219,22 @@ public class AppDbContext : DbContext
e.Property(a => a.WinRate7d).HasPrecision(8, 4);
e.Property(a => a.PnL24h).HasPrecision(18, 4);
e.Property(a => a.WinRate24h).HasPrecision(8, 4);
// D2c & E3 & E5
e.Property(a => a.MedianWinReturnPct).HasPrecision(18, 4);
e.Property(a => a.AvgWinReturnPct).HasPrecision(18, 4);
e.Property(a => a.MedianLossReturnPct).HasPrecision(18, 4);
e.Property(a => a.AvgLossReturnPct).HasPrecision(18, 4);
e.Property(a => a.ProfitFactor).HasPrecision(18, 4);
e.Property(a => a.MedianHoldDurationHours).HasPrecision(18, 4);
e.Property(a => a.P50PositionSize).HasPrecision(18, 4);
e.Property(a => a.P90PositionSize).HasPrecision(18, 4);
e.Property(a => a.TradesPerWeek).HasPrecision(18, 4);
e.Property(a => a.MedianMarketVolumeUsd).HasPrecision(18, 4);
e.Property(a => a.MedianPostFillDriftPct).HasPrecision(18, 4);
e.Property(a => a.NetEdgeAfterFeesPct).HasPrecision(18, 4);
});
// MarketAnalytics