@
#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> @
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
using Predictalytics.Application.Services;
|
||||
using Predictalytics.Domain.Entities;
|
||||
using Xunit;
|
||||
|
||||
namespace Predictalytics.Application.Tests.Services;
|
||||
|
||||
public class EdgeFreshnessCalculatorTests
|
||||
{
|
||||
private static TraderWindowMetrics Win(int closed, decimal avgReturn, decimal? pf = null)
|
||||
=> new() { ClosedMarkets = closed, AvgReturnPct = avgReturn, ProfitFactor = pf };
|
||||
|
||||
[Fact]
|
||||
public void TooFewMarkets_IsInsufficient()
|
||||
{
|
||||
var r = EdgeFreshnessCalculator.Compute(Win(3, 10m), Win(20, 8m));
|
||||
Assert.Equal("Insufficient", r.Verdict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HoldingEdge_IsFresh()
|
||||
{
|
||||
// Recent >= older and positive.
|
||||
var r = EdgeFreshnessCalculator.Compute(older: Win(30, 6m), recent: Win(25, 9m));
|
||||
Assert.Equal("Fresh", r.Verdict);
|
||||
Assert.True(r.Score > 50m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnCollapse_IsFading()
|
||||
{
|
||||
// Return per market dropped well beyond the fade threshold.
|
||||
var r = EdgeFreshnessCalculator.Compute(older: Win(30, 12m), recent: Win(25, 2m));
|
||||
Assert.Equal("Fading", r.Verdict);
|
||||
Assert.True(r.Score < 50m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfitFactorCollapse_IsFading_EvenOnSmallReturnDrop()
|
||||
{
|
||||
// Return barely moves, but a once-strong profit factor fell below break-even.
|
||||
var r = EdgeFreshnessCalculator.Compute(
|
||||
older: Win(30, 5m, pf: 1.6m),
|
||||
recent: Win(25, 4m, pf: 0.8m));
|
||||
Assert.Equal("Fading", r.Verdict);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MildDecline_IsStable()
|
||||
{
|
||||
// Small drop (< fade threshold), not an improvement -> Stable.
|
||||
var r = EdgeFreshnessCalculator.Compute(older: Win(30, 6m), recent: Win(25, 4m));
|
||||
Assert.Equal("Stable", r.Verdict);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user