Add BacktestSpotBot and update BacktestExecutor for spot trading support
- Introduced BacktestSpotBot class to handle backtesting for spot trading scenarios. - Updated BacktestExecutor to support both BacktestFutures and BacktestSpot trading types. - Enhanced error handling to provide clearer messages for unsupported trading types. - Registered new command handlers for OpenSpotPositionRequest and CloseSpotPositionCommand in ApiBootstrap. - Added unit tests for executing backtests with spot trading configurations, ensuring correct behavior and metrics validation.
This commit is contained in:
@@ -498,6 +498,101 @@ public class BacktestExecutorTests : BaseTests, IDisposable
|
||||
$"✅ Two-scenarios performance test passed: {candlesPerSecond:F1} candles/sec with {scenario.Indicators.Count} indicators");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteBacktestSpot_With_ETH_FifteenMinutes_Data_Should_Return_LightBacktest()
|
||||
{
|
||||
Console.WriteLine("TEST START: ExecuteBacktestSpot_With_ETH_FifteenMinutes_Data_Should_Return_LightBacktest");
|
||||
// Arrange
|
||||
var candles = FileHelpers.ReadJson<List<Candle>>("../../../Data/ETH-FifteenMinutes-candles.json");
|
||||
Assert.NotNull(candles);
|
||||
Assert.NotEmpty(candles);
|
||||
|
||||
var scenario = new Scenario("ETH_Spot_BacktestScenario");
|
||||
var rsiDivIndicator = ScenarioHelpers.BuildIndicator(IndicatorType.RsiDivergence, "RsiDiv", period: 14);
|
||||
scenario.Indicators = new List<IndicatorBase> { (IndicatorBase)rsiDivIndicator };
|
||||
scenario.LoopbackPeriod = 15;
|
||||
|
||||
var config = new TradingBotConfig
|
||||
{
|
||||
AccountName = _account.Name,
|
||||
MoneyManagement = MoneyManagement,
|
||||
Ticker = Ticker.ETH,
|
||||
Scenario = LightScenario.FromScenario(scenario),
|
||||
Timeframe = Timeframe.FifteenMinutes,
|
||||
IsForWatchingOnly = false,
|
||||
BotTradingBalance = 1000,
|
||||
TradingType = TradingType.BacktestSpot, // Use BacktestSpot instead of BacktestFutures
|
||||
CooldownPeriod = 1,
|
||||
MaxLossStreak = 0,
|
||||
FlipPosition = false,
|
||||
Name = "ETH_FifteenMinutes_Spot_Test",
|
||||
FlipOnlyWhenInProfit = true,
|
||||
MaxPositionTimeHours = null,
|
||||
CloseEarlyWhenProfitable = false
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = await _backtestExecutor.ExecuteAsync(
|
||||
config,
|
||||
candles,
|
||||
_testUser,
|
||||
save: false,
|
||||
withCandles: false,
|
||||
requestId: null,
|
||||
bundleRequestId: null,
|
||||
metadata: null,
|
||||
progressCallback: null);
|
||||
|
||||
// Output the result to console for review
|
||||
var json = JsonConvert.SerializeObject(new
|
||||
{
|
||||
result.FinalPnl,
|
||||
result.WinRate,
|
||||
result.GrowthPercentage,
|
||||
result.HodlPercentage,
|
||||
result.Fees,
|
||||
result.NetPnl,
|
||||
result.MaxDrawdown,
|
||||
result.SharpeRatio,
|
||||
result.Score,
|
||||
result.InitialBalance,
|
||||
StartDate = result.StartDate.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
EndDate = result.EndDate.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
TradingType = result.Config.TradingType
|
||||
}, Formatting.Indented);
|
||||
|
||||
Console.WriteLine("BacktestExecutor Spot Results:");
|
||||
Console.WriteLine(json);
|
||||
|
||||
// Debug: Verify telemetry is working
|
||||
Console.WriteLine($"DEBUG: Spot backtest completed successfully with {result.WinRate}% win rate");
|
||||
|
||||
// Assert - Validate specific backtest results
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<LightBacktest>(result);
|
||||
|
||||
// Verify TradingType is BacktestSpot
|
||||
Assert.Equal(TradingType.BacktestSpot, result.Config.TradingType);
|
||||
|
||||
// Validate key metrics - Updated with actual backtest results
|
||||
Assert.Equal(1000.0m, result.InitialBalance);
|
||||
Assert.Equal(-71.63m, Math.Round(result.FinalPnl, 2));
|
||||
Assert.Equal(16, result.WinRate);
|
||||
Assert.Equal(-10.86m, Math.Round(result.GrowthPercentage, 2));
|
||||
Assert.Equal(-0.67m, Math.Round(result.HodlPercentage, 2));
|
||||
Assert.Equal(32.59m, Math.Round(result.Fees, 2));
|
||||
Assert.Equal(-108.65m, Math.Round(result.NetPnl, 2));
|
||||
Assert.Equal(111.76m, Math.Round((decimal)result.MaxDrawdown, 2));
|
||||
Assert.Equal(-0.107, Math.Round((double)(result.SharpeRatio ?? 0), 3));
|
||||
Assert.True(Math.Abs(result.Score - 0.0) < 0.001,
|
||||
$"Score {result.Score} should be within 0.001 of expected value 0.0");
|
||||
|
||||
// Validate dates
|
||||
Assert.Equal(new DateTime(2025, 10, 14, 12, 0, 0), result.StartDate);
|
||||
Assert.Equal(new DateTime(2025, 10, 24, 11, 45, 0), result.EndDate);
|
||||
Assert.True(result.StartDate < result.EndDate);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_loggerFactory?.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user