using Xunit; using KArtSell.Modules.ModelOperations.ShadowRun; namespace KArtSell.Integration.Tests; /// /// Phase segmentation tests: regime classification + metrics per phase. /// Tests use production implementations from ShadowRun module. /// public sealed class PhaseSegmentationTests { [Fact] public void RegimeClassifier_BullTrend_ClassifiesAllAsBull() { // Arrange: Simulate bull market (5% increase) var bars = new List<(DateOnly, decimal)> { (new DateOnly(2024, 1, 2), 100m), (new DateOnly(2024, 1, 3), 101m), (new DateOnly(2024, 1, 4), 102m), (new DateOnly(2024, 1, 5), 103m), (new DateOnly(2024, 1, 8), 104m), (new DateOnly(2024, 1, 9), 105m), }; // Act var regimes = RegimeClassifier.Classify(bars); // Assert Assert.All(regimes, regime => Assert.Equal(MarketRegime.Bull, regime.Regime)); } [Fact] public void RegimeClassifier_BearTrend_ClassifiesAllAsBear() { // Arrange: Simulate bear market (4.76% decrease) var bars = new List<(DateOnly, decimal)> { (new DateOnly(2024, 1, 2), 105m), (new DateOnly(2024, 1, 3), 104m), (new DateOnly(2024, 1, 4), 103m), (new DateOnly(2024, 1, 5), 102m), (new DateOnly(2024, 1, 8), 101m), (new DateOnly(2024, 1, 9), 100m), }; // Act var regimes = RegimeClassifier.Classify(bars); // Assert Assert.All(regimes, regime => Assert.Equal(MarketRegime.Bear, regime.Regime)); } [Fact] public void RegimeClassifier_Sideways_ClassifiesAllAsSideways() { // Arrange: Simulate sideways market (0% net change) var bars = new List<(DateOnly, decimal)> { (new DateOnly(2024, 1, 2), 100m), (new DateOnly(2024, 1, 3), 101m), (new DateOnly(2024, 1, 4), 99m), (new DateOnly(2024, 1, 5), 102m), (new DateOnly(2024, 1, 8), 98m), (new DateOnly(2024, 1, 9), 100m), }; // Act var regimes = RegimeClassifier.Classify(bars); // Assert Assert.All(regimes, regime => Assert.Equal(MarketRegime.Sideways, regime.Regime)); } [Fact] public void PhaseMetrics_BullPhase_CalculatesCorrectMetrics() { // Arrange: 5 winning days var dailyReturns = new List { 0.01m, 0.02m, 0.01m, 0.005m, 0.015m }; // Act var metrics = PhaseMetricsCalculator.Calculate(dailyReturns); // Assert Assert.Equal(5, metrics.TradingDays); Assert.True(metrics.WinRate > 0 && metrics.WinRate <= 1); Assert.True(metrics.Return > 0, "Bull phase should have positive return"); } [Fact] public void PhaseMetrics_EmptyPhase_ReturnsZeros() { // Arrange: No returns var dailyReturns = new List(); // Act var metrics = PhaseMetricsCalculator.Calculate(dailyReturns); // Assert Assert.Equal(0, metrics.TradingDays); Assert.Equal(0m, metrics.Return); Assert.Equal(0m, metrics.Sharpe); } [Fact] public void PhaseMetrics_MixedReturns_CalculatesWinRate() { // Arrange: 3 wins, 2 losses var dailyReturns = new List { 0.01m, -0.005m, 0.02m, -0.01m, 0.015m }; // Act var metrics = PhaseMetricsCalculator.Calculate(dailyReturns); // Assert Assert.Equal(0.6m, metrics.WinRate); // 3/5 = 60% } [Fact] public void PhaseBreakdown_MultiPhase_SumsDaysCorrectly() { // Arrange: Multi-phase portfolio (bull days + bear days) var dailyReturns = new List<(DateOnly, decimal)> { (new DateOnly(2024, 1, 2), 0.01m), (new DateOnly(2024, 1, 3), 0.02m), (new DateOnly(2024, 1, 4), -0.005m), (new DateOnly(2024, 1, 5), 0.015m), (new DateOnly(2024, 1, 8), -0.01m), }; // Act var breakdown = PhaseSegmentation.Segment(dailyReturns); // Assert: Sum of trading days equals input count var totalDays = breakdown.BullMarket.TradingDays + breakdown.BearMarket.TradingDays + breakdown.Sideways.TradingDays + breakdown.HighVolatility.TradingDays; Assert.Equal(dailyReturns.Count, totalDays); } [Fact] public void Segmentation_ReturnsValidMetrics_AllFieldsPopulated() { // Arrange: Minimal multi-day scenario var dailyReturns = new List<(DateOnly, decimal)> { (new DateOnly(2024, 1, 2), 0.01m), (new DateOnly(2024, 1, 3), 0.02m), (new DateOnly(2024, 1, 4), -0.005m), }; // Act var breakdown = PhaseSegmentation.Segment(dailyReturns); // Assert: All metrics non-null and valid Assert.NotNull(breakdown.BullMarket); Assert.NotNull(breakdown.BearMarket); Assert.NotNull(breakdown.Sideways); Assert.NotNull(breakdown.HighVolatility); Assert.True(breakdown.BullMarket.WinRate >= 0 && breakdown.BullMarket.WinRate <= 1); Assert.True(breakdown.BullMarket.Sharpe >= -5 && breakdown.BullMarket.Sharpe <= 5); } }