68f3229c35
- Add SuspensionFreezeDetector via the IAnomalyDetector seam: a suspension gap with the favourite unchanged and a negligible (< threshold) price move — the mirror of the flip. Score = how completely the line froze. Reuses MatchWinEvidence so UI + evaluator handle it unchanged. 6 tests. - Add AnomalyKind.SuspensionFreeze + localized card/detail label, SuspensionFreezeThreshold option, and fan it into DetectAnomaliesUseCase.
55 lines
2.1 KiB
C#
55 lines
2.1 KiB
C#
namespace Marathon.Application.Configuration;
|
|
|
|
/// <summary>
|
|
/// Strongly typed options for the anomaly-detection subsystem.
|
|
/// Bound from the <c>Anomaly</c> section of <c>appsettings.json</c>.
|
|
/// </summary>
|
|
public sealed class AnomalyOptions
|
|
{
|
|
/// <summary>Configuration section key.</summary>
|
|
public const string SectionName = "Anomaly";
|
|
|
|
/// <summary>
|
|
/// Minimum gap between adjacent live snapshots, in seconds, to classify as
|
|
/// a bookmaker suspension. Default: 60 s.
|
|
/// </summary>
|
|
public int SuspensionGapSeconds { get; init; } = 60;
|
|
|
|
/// <summary>
|
|
/// Minimum normalised implied-probability delta required for the post-suspension
|
|
/// odds change to qualify as a flip. Must be in (0, 1). Default: 0.30.
|
|
/// </summary>
|
|
public decimal OddsFlipThreshold { get; init; } = 0.30m;
|
|
|
|
/// <summary>
|
|
/// Minimum number of live snapshots an event must have before detection runs.
|
|
/// Default: 3. Must be at least 2 (one pair).
|
|
/// </summary>
|
|
public int MinSnapshotCount { get; init; } = 3;
|
|
|
|
/// <summary>
|
|
/// How long the <c>AnomalyDetectionPoller</c> sleeps between detection cycles,
|
|
/// in seconds. Default: 60 s.
|
|
/// </summary>
|
|
public int DetectionIntervalSeconds { get; init; } = 60;
|
|
|
|
/// <summary>
|
|
/// Trailing window, in seconds, over which the steam-move detector measures a
|
|
/// continuous one-directional probability drift. Default: 120 s.
|
|
/// </summary>
|
|
public int SteamMoveWindowSeconds { get; init; } = 120;
|
|
|
|
/// <summary>
|
|
/// Minimum one-directional normalised implied-probability rise within the window
|
|
/// to flag a steam move. Must be in (0, 1). Default: 0.20 (20 percentage points).
|
|
/// </summary>
|
|
public decimal SteamMoveDriftThreshold { get; init; } = 0.20m;
|
|
|
|
/// <summary>
|
|
/// Maximum normalised implied-probability change across a suspension for it to count
|
|
/// as a "freeze" (line resumed essentially unchanged). Must be in (0, 1).
|
|
/// Default: 0.05 (5 percentage points).
|
|
/// </summary>
|
|
public decimal SuspensionFreezeThreshold { get; init; } = 0.05m;
|
|
}
|