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);
}
}