feat(wbs): WBS M4/M5 C# domain engines & Vue 3 PrimeVue AG-Grid migration [WBS-10]
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using QuantEngine.Core.Domain;
|
||||
|
||||
namespace QuantEngine.Core.Tests;
|
||||
|
||||
public class BacktesterTests
|
||||
{
|
||||
[Fact]
|
||||
public void RunBacktest_WithValidData_ReturnsCorrectMetrics()
|
||||
{
|
||||
// Arrange
|
||||
var backtester = new Backtester();
|
||||
var dailyValues = new List<decimal> { 100m, 102m, 101m, 105m, 108m, 110m };
|
||||
var trades = new List<BacktestTrade>
|
||||
{
|
||||
new BacktestTrade("005930", System.DateTime.UtcNow.AddDays(-5), System.DateTime.UtcNow, 100m, 110m, 10, 0.10m, 1.5m)
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = backtester.RunBacktest("test_run_01", dailyValues, trades, 1000m);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test_run_01", result.RunId);
|
||||
Assert.Equal("PASS", result.GateStatus);
|
||||
Assert.True(result.SharpeRatio > 0);
|
||||
Assert.True(result.MaxDrawdown >= 0 && result.MaxDrawdown <= 1);
|
||||
Assert.True(result.TurnoverRate > 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using QuantEngine.Core.Domain;
|
||||
|
||||
namespace QuantEngine.Core.Tests;
|
||||
|
||||
public class FactorWeightCalibratorTests
|
||||
{
|
||||
[Fact]
|
||||
public void CalibrateWeights_WithValidInputs_SatisfiesBoundsAndShrinkage()
|
||||
{
|
||||
// Arrange
|
||||
var calibrator = new FactorWeightCalibrator();
|
||||
var baseWeights = new Dictionary<string, decimal>
|
||||
{
|
||||
{ "F01_MOMENTUM", 0.30m },
|
||||
{ "F02_VOLATILITY", 0.20m }
|
||||
};
|
||||
var rawWeights = new Dictionary<string, decimal>
|
||||
{
|
||||
{ "F01_MOMENTUM", 0.60m }, // Out of bounds raw
|
||||
{ "F02_VOLATILITY", 0.10m }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = calibrator.CalibrateWeights("SS001_v1", baseWeights, rawWeights);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("SS001_v1", result.FormulaId);
|
||||
Assert.Equal("PASS", result.GateStatus);
|
||||
Assert.True(result.WeightsWithinBounds);
|
||||
Assert.True(result.OosComparisonReported);
|
||||
Assert.Equal(0.45m, result.Weights[0].CalibratedWeight); // Clamped to 0.30 * 1.5 = 0.45
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using QuantEngine.Core.Domain;
|
||||
|
||||
namespace QuantEngine.Core.Tests;
|
||||
|
||||
public class MarketRegimeDetectorTests
|
||||
{
|
||||
[Fact]
|
||||
public void DetectRegime_WithBullTrend_ReturnsBullLowVol()
|
||||
{
|
||||
// Arrange
|
||||
var detector = new MarketRegimeDetector();
|
||||
var prices = new List<decimal>();
|
||||
for (int i = 1; i <= 200; i++)
|
||||
{
|
||||
prices.Add(100m + i * 0.1m); // Steady uptrend
|
||||
}
|
||||
|
||||
// Act
|
||||
var result = detector.DetectRegime("2026-07-24", prices);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("2026-07-24", result.AsOfDate);
|
||||
Assert.Equal(MarketRegimeType.BULL_LOW_VOL, result.Regime);
|
||||
Assert.True(result.CurrentIndexPrice > result.Sma200);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using QuantEngine.Core.Domain;
|
||||
|
||||
namespace QuantEngine.Core.Tests;
|
||||
|
||||
public class PortfolioSizerTests
|
||||
{
|
||||
[Fact]
|
||||
public void CalculateTargetAllocations_WithValidScores_EnforcesCapsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var sizer = new PortfolioSizer();
|
||||
var scores = new Dictionary<string, decimal>
|
||||
{
|
||||
{ "005930", 80m },
|
||||
{ "000660", 20m }
|
||||
};
|
||||
var prices = new Dictionary<string, decimal>
|
||||
{
|
||||
{ "005930", 70000m },
|
||||
{ "000660", 120000m }
|
||||
};
|
||||
var vols = new Dictionary<string, decimal>();
|
||||
|
||||
// Act
|
||||
var packet = sizer.CalculateTargetAllocations("2026-07-24", 100000000m, 0.10m, scores, prices, vols);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("2026-07-24", packet.AsOfDate);
|
||||
Assert.True(packet.AllCapsSatisfied);
|
||||
Assert.Equal(10000000m, packet.ReservedCashKrw);
|
||||
Assert.Equal(2, packet.Allocations.Count);
|
||||
Assert.True(packet.Allocations[0].TargetWeightRatio <= 0.25m); // Cap enforced
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
using QuantEngine.Core.Domain;
|
||||
|
||||
namespace QuantEngine.Core.Tests;
|
||||
|
||||
public class WalkForwardEngineTests
|
||||
{
|
||||
[Fact]
|
||||
public void RunWalkForward_WithValidData_ReturnsMinimumFourWindowsAndPassesGate()
|
||||
{
|
||||
// Arrange
|
||||
var engine = new WalkForwardEngine();
|
||||
var dailyValues = new List<decimal>();
|
||||
for (int i = 0; i < 252 * 3; i++)
|
||||
{
|
||||
dailyValues.Add(100m + (i * 0.1m));
|
||||
}
|
||||
|
||||
// Act
|
||||
var result = engine.RunWalkForward("formula_ss001_v1", dailyValues, 4);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("formula_ss001_v1", result.FormulaId);
|
||||
Assert.Equal("PASS", result.GateStatus);
|
||||
Assert.True(result.TotalWindows >= 4);
|
||||
Assert.True(result.AverageOosSharpe > 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user