namespace Marathon.Domain.Backtesting; /// /// Parameters fed to . The strategy is "for every /// SuspensionFlip anomaly with score ≥ , stake /// according to on the post-flip favourite at the /// post-flip rate, then settle against the actual EventResult." /// /// /// Initial bankroll for compounding stake rules. Must be positive. /// /// /// Lower bound on Anomaly.Score — only anomalies at or above this /// threshold are bet on. Must be in [0, 1]. /// /// How to size each bet — see the enum docs. /// /// Used when is . /// Must be positive. /// /// /// Used when is /// . Expressed as a /// fraction in (0, 1]. e.g. 0.02 = 2 % of bankroll. /// /// /// Used when is . /// Multiplier on the raw Kelly fraction; in (0, 1]. 0.25 (quarter-Kelly) is /// the conservative default. /// public sealed record BacktestStrategy( decimal StartingBankroll, decimal MinScore, StakeRule StakeRule, decimal FlatStake, decimal PercentOfBankroll, decimal KellyFraction) { public decimal StartingBankroll { get; } = StartingBankroll > 0m ? StartingBankroll : throw new ArgumentOutOfRangeException(nameof(StartingBankroll), StartingBankroll, "StartingBankroll must be positive."); public decimal MinScore { get; } = MinScore is >= 0m and <= 1m ? MinScore : throw new ArgumentOutOfRangeException(nameof(MinScore), MinScore, "MinScore must be in [0, 1]."); public decimal FlatStake { get; } = FlatStake > 0m ? FlatStake : throw new ArgumentOutOfRangeException(nameof(FlatStake), FlatStake, "FlatStake must be positive."); public decimal PercentOfBankroll { get; } = PercentOfBankroll is > 0m and <= 1m ? PercentOfBankroll : throw new ArgumentOutOfRangeException(nameof(PercentOfBankroll), PercentOfBankroll, "PercentOfBankroll must be in (0, 1]."); public decimal KellyFraction { get; } = KellyFraction is > 0m and <= 1m ? KellyFraction : throw new ArgumentOutOfRangeException(nameof(KellyFraction), KellyFraction, "KellyFraction must be in (0, 1]."); /// Sensible defaults — flat-stake, score ≥ 0.45, ¼-Kelly waiting in the wings. public static BacktestStrategy Default { get; } = new( StartingBankroll: 1000m, MinScore: 0.45m, StakeRule: StakeRule.Flat, FlatStake: 50m, PercentOfBankroll: 0.02m, KellyFraction: 0.25m); }