Add bundle backtest
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
using System.Text.Json;
|
||||
using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Workers.Abstractions;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using Managing.Domain.Strategies;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
@@ -13,12 +16,11 @@ namespace Managing.Application.Workers;
|
||||
/// </summary>
|
||||
public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||
{
|
||||
private readonly IBacktestRepository _backtestRepository;
|
||||
// Removed direct repository usage for bundle requests
|
||||
private readonly IBacktester _backtester;
|
||||
private static readonly WorkerType _workerType = WorkerType.BundleBacktest;
|
||||
|
||||
public BundleBacktestWorker(
|
||||
IBacktestRepository backtestRepository,
|
||||
IBacktester backtester,
|
||||
ILogger<BundleBacktestWorker> logger,
|
||||
IWorkerService workerService) : base(
|
||||
@@ -27,7 +29,6 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||
TimeSpan.FromMinutes(1),
|
||||
workerService)
|
||||
{
|
||||
_backtestRepository = backtestRepository;
|
||||
_backtester = backtester;
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||
try
|
||||
{
|
||||
// Get pending bundle backtest requests
|
||||
var pendingRequests = _backtestRepository.GetPendingBundleBacktestRequests();
|
||||
var pendingRequests = _backtester.GetPendingBundleBacktestRequests();
|
||||
|
||||
foreach (var bundleRequest in pendingRequests)
|
||||
{
|
||||
@@ -61,10 +62,12 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||
|
||||
// Update status to running
|
||||
bundleRequest.Status = BundleBacktestRequestStatus.Running;
|
||||
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||
_backtester.UpdateBundleBacktestRequest(bundleRequest);
|
||||
|
||||
// Deserialize the backtest requests as dynamic objects
|
||||
var backtestRequests = JsonSerializer.Deserialize<List<JsonElement>>(bundleRequest.BacktestRequestsJson);
|
||||
// Deserialize the backtest requests as strongly-typed objects
|
||||
var backtestRequests =
|
||||
JsonSerializer.Deserialize<List<RunBacktestRequest>>(
|
||||
bundleRequest.BacktestRequestsJson);
|
||||
if (backtestRequests == null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to deserialize backtest requests");
|
||||
@@ -78,28 +81,27 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||
|
||||
try
|
||||
{
|
||||
var requestElement = backtestRequests[i];
|
||||
|
||||
var runBacktestRequest = backtestRequests[i];
|
||||
// Update current backtest being processed
|
||||
bundleRequest.CurrentBacktest = $"Backtest {i + 1} of {backtestRequests.Count}";
|
||||
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||
_backtester.UpdateBundleBacktestRequest(bundleRequest);
|
||||
|
||||
// Convert JSON element to domain model and run backtest
|
||||
await RunSingleBacktest(requestElement, bundleRequest.RequestId, cancellationToken);
|
||||
// Run the backtest directly with the strongly-typed request
|
||||
await RunSingleBacktest(runBacktestRequest, bundleRequest, i, cancellationToken);
|
||||
|
||||
// Update progress
|
||||
bundleRequest.CompletedBacktests++;
|
||||
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||
_backtester.UpdateBundleBacktestRequest(bundleRequest);
|
||||
|
||||
_logger.LogInformation("Completed backtest {Index} for bundle request {RequestId}",
|
||||
_logger.LogInformation("Completed backtest {Index} for bundle request {RequestId}",
|
||||
i + 1, bundleRequest.RequestId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing backtest {Index} for bundle request {RequestId}",
|
||||
_logger.LogError(ex, "Error processing backtest {Index} for bundle request {RequestId}",
|
||||
i + 1, bundleRequest.RequestId);
|
||||
bundleRequest.FailedBacktests++;
|
||||
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||
_backtester.UpdateBundleBacktestRequest(bundleRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,36 +123,120 @@ public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||
|
||||
bundleRequest.CompletedAt = DateTime.UtcNow;
|
||||
bundleRequest.CurrentBacktest = null;
|
||||
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||
_backtester.UpdateBundleBacktestRequest(bundleRequest);
|
||||
|
||||
_logger.LogInformation("Completed processing bundle backtest request {RequestId} with status {Status}",
|
||||
_logger.LogInformation("Completed processing bundle backtest request {RequestId} with status {Status}",
|
||||
bundleRequest.RequestId, bundleRequest.Status);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing bundle backtest request {RequestId}", bundleRequest.RequestId);
|
||||
|
||||
|
||||
bundleRequest.Status = BundleBacktestRequestStatus.Failed;
|
||||
bundleRequest.ErrorMessage = ex.Message;
|
||||
bundleRequest.CompletedAt = DateTime.UtcNow;
|
||||
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||
_backtester.UpdateBundleBacktestRequest(bundleRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunSingleBacktest(JsonElement requestElement, string bundleRequestId, CancellationToken cancellationToken)
|
||||
// Change RunSingleBacktest to accept RunBacktestRequest directly
|
||||
private async Task RunSingleBacktest(RunBacktestRequest runBacktestRequest, BundleBacktestRequest bundleRequest,
|
||||
int index, CancellationToken cancellationToken)
|
||||
{
|
||||
// For now, we'll use a simplified approach that simulates backtest execution
|
||||
// In a real implementation, you would parse the JSON and convert to domain models
|
||||
|
||||
// Simulate backtest processing time
|
||||
await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken);
|
||||
|
||||
_logger.LogInformation("Processed backtest for bundle request {RequestId}", bundleRequestId);
|
||||
|
||||
// TODO: Implement actual backtest execution by:
|
||||
// 1. Parsing the JSON request element
|
||||
// 2. Converting to TradingBotConfig domain model
|
||||
// 3. Calling _backtester.RunTradingBotBacktest with proper parameters
|
||||
// 4. Handling the results and saving to database if needed
|
||||
if (runBacktestRequest == null || runBacktestRequest.Config == null)
|
||||
{
|
||||
_logger.LogError("Invalid RunBacktestRequest in bundle (null config)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Map MoneyManagement
|
||||
MoneyManagement moneyManagement = null;
|
||||
if (!string.IsNullOrEmpty(runBacktestRequest.Config.MoneyManagementName))
|
||||
{
|
||||
// In worker context, we cannot resolve by name (no user/db), so skip or set null
|
||||
// Optionally, log a warning
|
||||
_logger.LogWarning("MoneyManagementName provided but cannot resolve in worker context: {Name}",
|
||||
(string)runBacktestRequest.Config.MoneyManagementName);
|
||||
}
|
||||
else if (runBacktestRequest.Config.MoneyManagement != null)
|
||||
{
|
||||
var mmReq = runBacktestRequest.Config.MoneyManagement;
|
||||
moneyManagement = new MoneyManagement
|
||||
{
|
||||
Name = mmReq.Name,
|
||||
Timeframe = mmReq.Timeframe,
|
||||
StopLoss = mmReq.StopLoss,
|
||||
TakeProfit = mmReq.TakeProfit,
|
||||
Leverage = mmReq.Leverage
|
||||
};
|
||||
moneyManagement.FormatPercentage();
|
||||
}
|
||||
|
||||
// Map Scenario
|
||||
Scenario scenario = null;
|
||||
if (runBacktestRequest.Config.Scenario != null)
|
||||
{
|
||||
var sReq = runBacktestRequest.Config.Scenario;
|
||||
scenario = new Scenario(sReq.Name, sReq.LoopbackPeriod)
|
||||
{
|
||||
User = null // No user context in worker
|
||||
};
|
||||
foreach (var indicatorRequest in sReq.Indicators)
|
||||
{
|
||||
var indicator = new Indicator(indicatorRequest.Name, indicatorRequest.Type)
|
||||
{
|
||||
SignalType = indicatorRequest.SignalType,
|
||||
MinimumHistory = indicatorRequest.MinimumHistory,
|
||||
Period = indicatorRequest.Period,
|
||||
FastPeriods = indicatorRequest.FastPeriods,
|
||||
SlowPeriods = indicatorRequest.SlowPeriods,
|
||||
SignalPeriods = indicatorRequest.SignalPeriods,
|
||||
Multiplier = indicatorRequest.Multiplier,
|
||||
SmoothPeriods = indicatorRequest.SmoothPeriods,
|
||||
StochPeriods = indicatorRequest.StochPeriods,
|
||||
CyclePeriods = indicatorRequest.CyclePeriods,
|
||||
User = null // No user context in worker
|
||||
};
|
||||
scenario.AddIndicator(indicator);
|
||||
}
|
||||
}
|
||||
|
||||
// Map TradingBotConfig
|
||||
var backtestConfig = new TradingBotConfig
|
||||
{
|
||||
AccountName = runBacktestRequest.Config.AccountName,
|
||||
MoneyManagement = moneyManagement,
|
||||
Ticker = runBacktestRequest.Config.Ticker,
|
||||
ScenarioName = runBacktestRequest.Config.ScenarioName,
|
||||
Scenario = scenario,
|
||||
Timeframe = runBacktestRequest.Config.Timeframe,
|
||||
IsForWatchingOnly = runBacktestRequest.Config.IsForWatchingOnly,
|
||||
BotTradingBalance = runBacktestRequest.Config.BotTradingBalance,
|
||||
IsForBacktest = true,
|
||||
CooldownPeriod = runBacktestRequest.Config.CooldownPeriod,
|
||||
MaxLossStreak = runBacktestRequest.Config.MaxLossStreak,
|
||||
MaxPositionTimeHours = runBacktestRequest.Config.MaxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = runBacktestRequest.Config.FlipOnlyWhenInProfit,
|
||||
FlipPosition = runBacktestRequest.Config.FlipPosition,
|
||||
Name = $"{bundleRequest.Name} #{index + 1}",
|
||||
CloseEarlyWhenProfitable = runBacktestRequest.Config.CloseEarlyWhenProfitable,
|
||||
UseSynthApi = runBacktestRequest.Config.UseSynthApi,
|
||||
UseForPositionSizing = runBacktestRequest.Config.UseForPositionSizing,
|
||||
UseForSignalFiltering = runBacktestRequest.Config.UseForSignalFiltering,
|
||||
UseForDynamicStopLoss = runBacktestRequest.Config.UseForDynamicStopLoss
|
||||
};
|
||||
|
||||
// Run the backtest (no user context)
|
||||
var result = await _backtester.RunTradingBotBacktest(
|
||||
backtestConfig,
|
||||
runBacktestRequest.StartDate,
|
||||
runBacktestRequest.EndDate,
|
||||
null, // No user context in worker
|
||||
runBacktestRequest.Save,
|
||||
runBacktestRequest.WithCandles,
|
||||
bundleRequest.RequestId // Use bundleRequestId as requestId for traceability
|
||||
);
|
||||
|
||||
_logger.LogInformation("Processed backtest for bundle request {RequestId}", bundleRequest.RequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user