Add precalculated signals list + multi scenario test
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user