From a8f55c80a90437be91d71c18f9056e5ee699d2d2 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Wed, 12 Nov 2025 22:40:58 +0700 Subject: [PATCH] Fix bundle completion --- .../Backtests/BacktestExecutor.cs | 150 ------------------ .../Workers/BacktestComputeWorker.cs | 13 +- 2 files changed, 12 insertions(+), 151 deletions(-) diff --git a/src/Managing.Application/Backtests/BacktestExecutor.cs b/src/Managing.Application/Backtests/BacktestExecutor.cs index b835c295..ee7bf5ba 100644 --- a/src/Managing.Application/Backtests/BacktestExecutor.cs +++ b/src/Managing.Application/Backtests/BacktestExecutor.cs @@ -410,12 +410,6 @@ public class BacktestExecutor if (save && user != null) { await _backtestRepository.InsertBacktestForUserAsync(user, result); - - // Update bundle request if provided - if (bundleRequestId.HasValue) - { - await UpdateBundleRequestWithBacktestResult(user, bundleRequestId.Value, result); - } } // Send notification if backtest meets criteria @@ -574,150 +568,6 @@ public class BacktestExecutor return tradingBot; } - /// - /// Optimized backtest step execution - delegate to standard Run but with backtest optimizations - /// - private async Task RunOptimizedBacktestStep(TradingBotBase tradingBot) - { - // Use the standard Run method but ensure it's optimized for backtests - } - - - /// - /// Updates bundle request with the completed backtest result - /// - private async Task UpdateBundleRequestWithBacktestResult(User user, Guid bundleRequestId, Backtest backtest) - { - try - { - using var scope = _scopeFactory.CreateScope(); - var backtestRepository = scope.ServiceProvider.GetRequiredService(); - var jobRepository = scope.ServiceProvider.GetRequiredService(); - var webhookService = scope.ServiceProvider.GetRequiredService(); - - // Get bundle request - var bundleRequest = backtestRepository.GetBundleBacktestRequestByIdForUser(user, bundleRequestId); - if (bundleRequest == null) - { - _logger.LogWarning("Bundle request {BundleRequestId} not found for user {UserId}", bundleRequestId, - user.Id); - return; - } - - var previousStatus = bundleRequest.Status; - - // Get all jobs for this bundle to calculate progress - var jobs = await jobRepository.GetByBundleRequestIdAsync(bundleRequestId); - var completedJobs = jobs.Count(j => j.Status == JobStatus.Completed); - var failedJobs = jobs.Count(j => j.Status == JobStatus.Failed); - var runningJobs = jobs.Count(j => j.Status == JobStatus.Running); - var totalJobs = jobs.Count(); - - // Update bundle request progress (always update counters regardless of status) - bundleRequest.CompletedBacktests = completedJobs; - bundleRequest.FailedBacktests = failedJobs; - bundleRequest.UpdatedAt = DateTime.UtcNow; - - // CRITICAL: If bundle is already in a final state (Completed/Failed with CompletedAt set), - // don't overwrite it unless we're detecting a legitimate change - if (bundleRequest.CompletedAt.HasValue && - (bundleRequest.Status == BundleBacktestRequestStatus.Completed || - bundleRequest.Status == BundleBacktestRequestStatus.Failed)) - { - // Bundle already finalized, only update if job counts indicate it should be re-opened - // (This shouldn't happen in normal flow, but guards against race conditions) - if (completedJobs + failedJobs == totalJobs) - { - _logger.LogDebug( - "Bundle {BundleRequestId} already completed/failed. Skipping status update.", - bundleRequestId); - // Progress counters already updated above, just return - return; - } - else - { - _logger.LogWarning( - "Bundle {BundleRequestId} was marked as completed/failed but has incomplete jobs ({Completed}+{Failed}/{Total}). Reopening.", - bundleRequestId, completedJobs, failedJobs, totalJobs); - // Allow the update to proceed to fix inconsistent state - } - } - - // Update status based on job states - if (completedJobs + failedJobs == totalJobs) - { - // All jobs completed or failed - if (failedJobs == 0) - { - bundleRequest.Status = BundleBacktestRequestStatus.Completed; - } - else if (completedJobs == 0) - { - bundleRequest.Status = BundleBacktestRequestStatus.Failed; - bundleRequest.ErrorMessage = "All backtests failed"; - } - else - { - bundleRequest.Status = BundleBacktestRequestStatus.Completed; - bundleRequest.ErrorMessage = $"{failedJobs} backtests failed"; - } - - bundleRequest.CompletedAt = DateTime.UtcNow; - bundleRequest.CurrentBacktest = null; - } - else if (runningJobs > 0 || completedJobs > 0 || failedJobs > 0) - { - // Some jobs are running, or some have completed/failed (meaning work has started) - // Once a bundle has started processing, it should stay "Running" until all jobs are done - bundleRequest.Status = BundleBacktestRequestStatus.Running; - } - // If all jobs are still pending (completedJobs = 0, failedJobs = 0, runningJobs = 0), - // keep the current status (likely Pending) - - // Update results list with the new backtest ID - var resultsList = bundleRequest.Results?.ToList() ?? new List(); - if (!resultsList.Contains(backtest.Id)) - { - resultsList.Add(backtest.Id); - bundleRequest.Results = resultsList; - } - - await backtestRepository.UpdateBundleBacktestRequestAsync(bundleRequest); - - // Send webhook notification if bundle request just completed - if (previousStatus != BundleBacktestRequestStatus.Completed && - bundleRequest.Status == BundleBacktestRequestStatus.Completed && - !string.IsNullOrEmpty(user.TelegramChannel)) - { - var message = - $"✅ Bundle backtest '{bundleRequest.Name}' (ID: {bundleRequest.RequestId}) completed successfully. " + - $"Completed: {completedJobs}/{totalJobs} backtests" + - (failedJobs > 0 ? $", Failed: {failedJobs}" : "") + - $". Results: {resultsList.Count} backtest(s) generated."; - - await webhookService.SendMessage(message, user.TelegramChannel); - } - else if (previousStatus != BundleBacktestRequestStatus.Failed && - bundleRequest.Status == BundleBacktestRequestStatus.Failed && - !string.IsNullOrEmpty(user.TelegramChannel)) - { - var message = $"❌ Bundle backtest '{bundleRequest.Name}' (ID: {bundleRequest.RequestId}) failed. " + - $"All {totalJobs} backtests failed. Error: {bundleRequest.ErrorMessage}"; - - await webhookService.SendMessage(message, user.TelegramChannel); - } - - _logger.LogInformation( - "Updated bundle request {BundleRequestId} with backtest {BacktestId}: {Completed}/{Total} completed, {Failed} failed, {Running} running", - bundleRequestId, backtest.Id, completedJobs, totalJobs, failedJobs, runningJobs); - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to update bundle request {BundleRequestId} with backtest {BacktestId}", - bundleRequestId, backtest.Id); - } - } - /// /// Sends notification if backtest meets criteria /// diff --git a/src/Managing.Application/Workers/BacktestComputeWorker.cs b/src/Managing.Application/Workers/BacktestComputeWorker.cs index c9f5d5a2..e3f899d0 100644 --- a/src/Managing.Application/Workers/BacktestComputeWorker.cs +++ b/src/Managing.Application/Workers/BacktestComputeWorker.cs @@ -343,7 +343,18 @@ public class BacktestComputeWorker : BackgroundService "Completed backtest job {JobId}. Score: {Score}, PnL: {PnL}, Duration: {DurationMinutes:F1} minutes", job.Id, result.Score, result.FinalPnl, elapsedTime.TotalMinutes); - // Bundle request is now updated in the BacktestExecutor + // Update bundle request progress if this job is part of a bundle + if (job.BundleRequestId.HasValue) + { + try + { + await UpdateBundleRequestProgress(job.BundleRequestId.Value, scope.ServiceProvider); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error updating bundle request progress for job {JobId}", job.Id); + } + } } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) {