Add genetic backtest to worker
This commit is contained in:
149
src/Managing.Application/Backtests/GeneticExecutor.cs
Normal file
149
src/Managing.Application/Backtests/GeneticExecutor.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Domain.Backtests;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Managing.Application.Backtests;
|
||||
|
||||
/// <summary>
|
||||
/// Service for executing genetic algorithm requests without Orleans dependencies.
|
||||
/// Extracted from GeneticBacktestGrain to be reusable in compute workers.
|
||||
/// </summary>
|
||||
public class GeneticExecutor
|
||||
{
|
||||
private readonly ILogger<GeneticExecutor> _logger;
|
||||
private readonly IGeneticService _geneticService;
|
||||
private readonly IAccountService _accountService;
|
||||
private readonly IWebhookService _webhookService;
|
||||
|
||||
public GeneticExecutor(
|
||||
ILogger<GeneticExecutor> logger,
|
||||
IGeneticService geneticService,
|
||||
IAccountService accountService,
|
||||
IWebhookService webhookService)
|
||||
{
|
||||
_logger = logger;
|
||||
_geneticService = geneticService;
|
||||
_accountService = accountService;
|
||||
_webhookService = webhookService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes a genetic algorithm request.
|
||||
/// </summary>
|
||||
/// <param name="geneticRequestId">The genetic request ID to process</param>
|
||||
/// <param name="progressCallback">Optional callback for progress updates (0-100)</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>The genetic algorithm result</returns>
|
||||
public async Task<GeneticAlgorithmResult> ExecuteAsync(
|
||||
string geneticRequestId,
|
||||
Func<int, Task> progressCallback = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Load the request by status lists and filter by ID (Pending first, then Failed for retries)
|
||||
var pending = await _geneticService.GetGeneticRequestsAsync(GeneticRequestStatus.Pending);
|
||||
var failed = await _geneticService.GetGeneticRequestsAsync(GeneticRequestStatus.Failed);
|
||||
var request = pending.Concat(failed).FirstOrDefault(r => r.RequestId == geneticRequestId);
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
_logger.LogWarning("[GeneticExecutor] Request {RequestId} not found among pending/failed.",
|
||||
geneticRequestId);
|
||||
throw new InvalidOperationException($"Genetic request {geneticRequestId} not found");
|
||||
}
|
||||
|
||||
_logger.LogInformation("[GeneticExecutor] Processing genetic request {RequestId} for user {UserId}",
|
||||
request.RequestId, request.User.Id);
|
||||
|
||||
// Mark running
|
||||
request.Status = GeneticRequestStatus.Running;
|
||||
await _geneticService.UpdateGeneticRequestAsync(request);
|
||||
|
||||
// Load user accounts if not already loaded
|
||||
if (request.User.Accounts == null || !request.User.Accounts.Any())
|
||||
{
|
||||
request.User.Accounts = (await _accountService.GetAccountsByUserAsync(request.User)).ToList();
|
||||
}
|
||||
|
||||
// Create progress wrapper if callback provided
|
||||
Func<int, Task> wrappedProgressCallback = null;
|
||||
if (progressCallback != null)
|
||||
{
|
||||
wrappedProgressCallback = async (percentage) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await progressCallback(percentage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Error in progress callback for genetic request {RequestId}", geneticRequestId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Run GA
|
||||
var result = await _geneticService.RunGeneticAlgorithm(request, cancellationToken);
|
||||
|
||||
// Update final state
|
||||
request.Status = GeneticRequestStatus.Completed;
|
||||
request.CompletedAt = DateTime.UtcNow;
|
||||
request.BestFitness = result.BestFitness;
|
||||
request.BestIndividual = result.BestIndividual;
|
||||
request.ProgressInfo = result.ProgressInfo;
|
||||
await _geneticService.UpdateGeneticRequestAsync(request);
|
||||
|
||||
_logger.LogInformation("[GeneticExecutor] Completed genetic request {RequestId}. Best Fitness: {BestFitness}",
|
||||
request.RequestId, result.BestFitness);
|
||||
|
||||
// Send webhook notification if user has telegram channel
|
||||
if (!string.IsNullOrEmpty(request.User?.TelegramChannel))
|
||||
{
|
||||
var message = $"✅ Genetic algorithm optimization completed for {request.Ticker} on {request.Timeframe}. " +
|
||||
$"Request ID: {request.RequestId}. " +
|
||||
$"Best Fitness: {result.BestFitness:F4}. " +
|
||||
$"Generations: {request.Generations}, Population: {request.PopulationSize}.";
|
||||
|
||||
await _webhookService.SendMessage(message, request.User.TelegramChannel);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "[GeneticExecutor] Error processing genetic request {RequestId}", geneticRequestId);
|
||||
|
||||
// Try to mark as failed
|
||||
try
|
||||
{
|
||||
var running = await _geneticService.GetGeneticRequestsAsync(GeneticRequestStatus.Running);
|
||||
var req = running.FirstOrDefault(r => r.RequestId == geneticRequestId);
|
||||
if (req != null)
|
||||
{
|
||||
req.Status = GeneticRequestStatus.Failed;
|
||||
req.ErrorMessage = ex.Message;
|
||||
req.CompletedAt = DateTime.UtcNow;
|
||||
await _geneticService.UpdateGeneticRequestAsync(req);
|
||||
|
||||
// Send webhook notification for failed genetic request
|
||||
if (!string.IsNullOrEmpty(req.User?.TelegramChannel))
|
||||
{
|
||||
var message = $"❌ Genetic algorithm optimization failed for {req.Ticker} on {req.Timeframe}. " +
|
||||
$"Request ID: {req.RequestId}. " +
|
||||
$"Error: {ex.Message}";
|
||||
|
||||
await _webhookService.SendMessage(message, req.User.TelegramChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception updateEx)
|
||||
{
|
||||
_logger.LogError(updateEx, "[GeneticExecutor] Failed to update request status to Failed for {RequestId}", geneticRequestId);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user