Add precalculated signals list + multi scenario test

This commit is contained in:
2025-11-11 14:05:09 +07:00
parent e810ab60ce
commit 903413692c
7 changed files with 359 additions and 17 deletions

View File

@@ -372,6 +372,109 @@ public class BacktestExecutorTests : BaseTests, IDisposable
Console.WriteLine($"✅ Performance test passed: {candlesPerSecond:F1} candles/sec");
}
[Fact]
public async Task ExecuteBacktest_With_Two_Scenarios_Should_Show_Performance_Telemetry()
{
// Arrange - Test with 2 indicators to verify pre-calculated signals optimization works with multiple scenarios
var candles =
FileHelpers.ReadJson<List<Candle>>("../../../Data/ETH-FifteenMinutes-candles-20:44:15 +00:00-.json");
Assert.NotNull(candles);
Assert.NotEmpty(candles);
Console.WriteLine($"DEBUG: Loaded {candles.Count} candles for two-scenarios performance telemetry test");
var scenario = new Scenario("ETH_TwoScenarios_Backtest");
var rsiDivIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.RsiDivergence, "RsiDiv", period: 14);
var emaCrossIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.EmaCross, "EmaCross", period: 21);
scenario.Indicators = new List<IndicatorBase> { (IndicatorBase)rsiDivIndicator, (IndicatorBase)emaCrossIndicator };
scenario.LoopbackPeriod = 15; // 15 minutes loopback period as requested
var config = new TradingBotConfig
{
AccountName = _account.Name,
MoneyManagement = MoneyManagement,
Ticker = Ticker.ETH,
Scenario = LightScenario.FromScenario(scenario),
Timeframe = Timeframe.FifteenMinutes,
IsForWatchingOnly = false,
BotTradingBalance = 100000,
IsForBacktest = true,
CooldownPeriod = 1,
MaxLossStreak = 0,
FlipPosition = false,
Name = "ETH_TwoScenarios_Performance_Test",
FlipOnlyWhenInProfit = true,
MaxPositionTimeHours = null,
CloseEarlyWhenProfitable = false
};
// Track execution time
var startTime = DateTime.UtcNow;
// Act
var result = await _backtestExecutor.ExecuteAsync(
config,
candles.ToHashSet(),
_testUser,
save: false,
withCandles: false,
requestId: null,
bundleRequestId: null,
metadata: null,
progressCallback: null);
var executionTime = DateTime.UtcNow - startTime;
// Assert - Verify the result is valid
Assert.NotNull(result);
Assert.Equal(Ticker.ETH, result.Config.Ticker);
Assert.Equal(100000, result.InitialBalance);
Assert.True(result.Score >= 0);
// Business Logic Baseline Assertions - ensure consistency over time
// These values establish the expected baseline for the two-scenarios test
const decimal expectedFinalPnl = 2018.27m;
const double expectedScore = 19.18;
const int expectedWinRatePercent = 40; // 40% win rate
const decimal expectedGrowthPercentage = 2.02m;
// Allow small tolerance for floating-point precision variations
const decimal pnlTolerance = 0.01m;
const double scoreTolerance = 0.01;
const decimal growthTolerance = 0.01m;
Assert.True(Math.Abs(result.FinalPnl - expectedFinalPnl) <= pnlTolerance,
$"Final PnL {result.FinalPnl:F2} differs from expected baseline {expectedFinalPnl:F2} (tolerance: ±{pnlTolerance:F2})");
Assert.True(Math.Abs(result.Score - expectedScore) <= scoreTolerance,
$"Score {result.Score:F2} differs from expected baseline {expectedScore:F2} (tolerance: ±{scoreTolerance:F2})");
Assert.True(Math.Abs(result.WinRate - expectedWinRatePercent) <= 5,
$"Win Rate {result.WinRate}% differs from expected baseline {expectedWinRatePercent}% (tolerance: ±5%)");
Assert.True(Math.Abs(result.GrowthPercentage - expectedGrowthPercentage) <= growthTolerance,
$"Growth {result.GrowthPercentage:F2}% differs from expected baseline {expectedGrowthPercentage:F2}% (tolerance: ±{growthTolerance:F2}%)");
// Performance metrics
var totalCandles = candles.Count;
var candlesPerSecond = totalCandles / executionTime.TotalSeconds;
// Log comprehensive performance metrics
Console.WriteLine($"📊 === TWO-SCENARIOS PERFORMANCE TELEMETRY ===");
Console.WriteLine($"⏱️ Total Execution Time: {executionTime.TotalSeconds:F2}s");
Console.WriteLine($"📈 Candles Processed: {totalCandles} ({candlesPerSecond:F1} candles/sec)");
Console.WriteLine($"🎯 Final PnL: {result.FinalPnl:F2} (Expected: {expectedFinalPnl:F2})");
Console.WriteLine($"📊 Score: {result.Score:F2} (Expected: {expectedScore:F2})");
Console.WriteLine($"📈 Win Rate: {result.WinRate}% (Expected: {expectedWinRatePercent}%)");
Console.WriteLine($"📈 Growth: {result.GrowthPercentage:F2}% (Expected: {expectedGrowthPercentage:F2}%)");
Console.WriteLine($"🎭 Scenario: {scenario.Name} ({scenario.Indicators.Count} indicators, LoopbackPeriod: {scenario.LoopbackPeriod})");
// Performance assertion - should be reasonably fast even with 2 indicators
Assert.True(candlesPerSecond > 200, $"Expected >200 candles/sec with 2 indicators, got {candlesPerSecond:F1} candles/sec");
Console.WriteLine($"✅ Two-scenarios performance test passed: {candlesPerSecond:F1} candles/sec with {scenario.Indicators.Count} indicators");
}
public void Dispose()
{
_loggerFactory?.Dispose();