Setup bundle for backtest
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
using Managing.Api.Models.Requests;
|
using System.Text.Json;
|
||||||
|
using Managing.Api.Models.Requests;
|
||||||
using Managing.Application.Abstractions;
|
using Managing.Application.Abstractions;
|
||||||
|
using Managing.Application.Abstractions.Repositories;
|
||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
using Managing.Application.Hubs;
|
using Managing.Application.Hubs;
|
||||||
using Managing.Domain.Backtests;
|
using Managing.Domain.Backtests;
|
||||||
@@ -31,6 +33,7 @@ public class BacktestController : BaseController
|
|||||||
private readonly IAccountService _accountService;
|
private readonly IAccountService _accountService;
|
||||||
private readonly IMoneyManagementService _moneyManagementService;
|
private readonly IMoneyManagementService _moneyManagementService;
|
||||||
private readonly IGeneticService _geneticService;
|
private readonly IGeneticService _geneticService;
|
||||||
|
private readonly IBacktestRepository _backtestRepository;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BacktestController"/> class.
|
/// Initializes a new instance of the <see cref="BacktestController"/> class.
|
||||||
@@ -41,6 +44,7 @@ public class BacktestController : BaseController
|
|||||||
/// <param name="accountService">The service for account management.</param>
|
/// <param name="accountService">The service for account management.</param>
|
||||||
/// <param name="moneyManagementService">The service for money management strategies.</param>
|
/// <param name="moneyManagementService">The service for money management strategies.</param>
|
||||||
/// <param name="geneticService">The service for genetic algorithm operations.</param>
|
/// <param name="geneticService">The service for genetic algorithm operations.</param>
|
||||||
|
/// <param name="backtestRepository">The repository for backtest operations.</param>
|
||||||
public BacktestController(
|
public BacktestController(
|
||||||
IHubContext<BotHub> hubContext,
|
IHubContext<BotHub> hubContext,
|
||||||
IBacktester backtester,
|
IBacktester backtester,
|
||||||
@@ -48,6 +52,7 @@ public class BacktestController : BaseController
|
|||||||
IAccountService accountService,
|
IAccountService accountService,
|
||||||
IMoneyManagementService moneyManagementService,
|
IMoneyManagementService moneyManagementService,
|
||||||
IGeneticService geneticService,
|
IGeneticService geneticService,
|
||||||
|
IBacktestRepository backtestRepository,
|
||||||
IUserService userService) : base(userService)
|
IUserService userService) : base(userService)
|
||||||
{
|
{
|
||||||
_hubContext = hubContext;
|
_hubContext = hubContext;
|
||||||
@@ -56,6 +61,7 @@ public class BacktestController : BaseController
|
|||||||
_accountService = accountService;
|
_accountService = accountService;
|
||||||
_moneyManagementService = moneyManagementService;
|
_moneyManagementService = moneyManagementService;
|
||||||
_geneticService = geneticService;
|
_geneticService = geneticService;
|
||||||
|
_backtestRepository = backtestRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -400,14 +406,14 @@ public class BacktestController : BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs multiple backtests in a bundle with the specified configurations.
|
/// Creates a bundle backtest request with the specified configurations.
|
||||||
/// This endpoint receives a list of backtest requests and will execute them all.
|
/// This endpoint creates a request that will be processed by a background worker.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requests">The list of backtest requests to execute.</param>
|
/// <param name="requests">The list of backtest requests to execute.</param>
|
||||||
/// <returns>A list of backtest results.</returns>
|
/// <returns>The bundle backtest request with ID for tracking progress.</returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("Bundle")]
|
[Route("Bundle")]
|
||||||
public async Task<ActionResult<IEnumerable<object>>> RunBundle([FromBody] List<RunBacktestRequest> requests)
|
public async Task<ActionResult<BundleBacktestRequest>> RunBundle([FromBody] List<RunBacktestRequest> requests)
|
||||||
{
|
{
|
||||||
if (requests == null || !requests.Any())
|
if (requests == null || !requests.Any())
|
||||||
{
|
{
|
||||||
@@ -419,92 +425,110 @@ public class BacktestController : BaseController
|
|||||||
return BadRequest("Maximum of 10 backtests allowed per bundle request");
|
return BadRequest("Maximum of 10 backtests allowed per bundle request");
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await GetUser();
|
try
|
||||||
var results = new List<object>();
|
|
||||||
|
|
||||||
foreach (var request in requests)
|
|
||||||
{
|
{
|
||||||
try
|
var user = await GetUser();
|
||||||
|
|
||||||
|
// Validate all requests before creating the bundle
|
||||||
|
foreach (var request in requests)
|
||||||
{
|
{
|
||||||
// Validate individual request
|
|
||||||
if (request?.Config == null)
|
if (request?.Config == null)
|
||||||
{
|
{
|
||||||
results.Add(new
|
return BadRequest("Invalid request: Configuration is required");
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
Status = "Error",
|
|
||||||
Message = "Invalid request: Configuration is required"
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(request.Config.AccountName))
|
if (string.IsNullOrEmpty(request.Config.AccountName))
|
||||||
{
|
{
|
||||||
results.Add(new
|
return BadRequest("Invalid request: Account name is required");
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
Status = "Error",
|
|
||||||
Message = "Invalid request: Account name is required"
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(request.Config.ScenarioName) && request.Config.Scenario == null)
|
if (string.IsNullOrEmpty(request.Config.ScenarioName) && request.Config.Scenario == null)
|
||||||
{
|
{
|
||||||
results.Add(new
|
return BadRequest("Invalid request: Either scenario name or scenario object is required");
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
Status = "Error",
|
|
||||||
Message = "Invalid request: Either scenario name or scenario object is required"
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(request.Config.MoneyManagementName) && request.Config.MoneyManagement == null)
|
if (string.IsNullOrEmpty(request.Config.MoneyManagementName) && request.Config.MoneyManagement == null)
|
||||||
{
|
{
|
||||||
results.Add(new
|
return BadRequest("Invalid request: Either money management name or money management object is required");
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
Status = "Error",
|
|
||||||
Message = "Invalid request: Either money management name or money management object is required"
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement actual backtest execution logic here
|
|
||||||
// For now, return a placeholder result
|
|
||||||
var placeholderResult = new
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid().ToString(),
|
|
||||||
Status = "Pending",
|
|
||||||
Message = "Bundle backtest - Application layer integration pending",
|
|
||||||
Config = new
|
|
||||||
{
|
|
||||||
AccountName = request.Config.AccountName,
|
|
||||||
Ticker = request.Config.Ticker,
|
|
||||||
ScenarioName = request.Config.ScenarioName,
|
|
||||||
Timeframe = request.Config.Timeframe,
|
|
||||||
BotTradingBalance = request.Config.BotTradingBalance,
|
|
||||||
Name = request.Config.Name ?? $"Bundle-Backtest-{DateTime.UtcNow:yyyyMMdd-HHmmss}"
|
|
||||||
},
|
|
||||||
StartDate = request.StartDate,
|
|
||||||
EndDate = request.EndDate
|
|
||||||
};
|
|
||||||
|
|
||||||
results.Add(placeholderResult);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
|
// Create the bundle backtest request
|
||||||
|
var bundleRequest = new BundleBacktestRequest
|
||||||
{
|
{
|
||||||
results.Add(new
|
User = user,
|
||||||
{
|
BacktestRequestsJson = JsonSerializer.Serialize(requests),
|
||||||
Id = Guid.NewGuid().ToString(),
|
TotalBacktests = requests.Count,
|
||||||
Status = "Error",
|
CompletedBacktests = 0,
|
||||||
Message = $"Error processing backtest: {ex.Message}"
|
FailedBacktests = 0,
|
||||||
});
|
Status = BundleBacktestRequestStatus.Pending
|
||||||
}
|
};
|
||||||
|
|
||||||
|
_backtestRepository.InsertBundleBacktestRequestForUser(user, bundleRequest);
|
||||||
|
|
||||||
|
return Ok(bundleRequest);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return StatusCode(500, $"Error creating bundle backtest request: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves all bundle backtest requests for the authenticated user.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A list of bundle backtest requests with their current status.</returns>
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Bundle")]
|
||||||
|
public async Task<ActionResult<IEnumerable<BundleBacktestRequest>>> GetBundleBacktestRequests()
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
var bundleRequests = _backtestRepository.GetBundleBacktestRequestsByUser(user);
|
||||||
|
return Ok(bundleRequests);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a specific bundle backtest request by ID for the authenticated user.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The ID of the bundle backtest request to retrieve.</param>
|
||||||
|
/// <returns>The requested bundle backtest request with current status and results.</returns>
|
||||||
|
[HttpGet]
|
||||||
|
[Route("Bundle/{id}")]
|
||||||
|
public async Task<ActionResult<BundleBacktestRequest>> GetBundleBacktestRequest(string id)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
var bundleRequest = _backtestRepository.GetBundleBacktestRequestByIdForUser(user, id);
|
||||||
|
|
||||||
|
if (bundleRequest == null)
|
||||||
|
{
|
||||||
|
return NotFound($"Bundle backtest request with ID {id} not found or doesn't belong to the current user.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(results);
|
return Ok(bundleRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes a specific bundle backtest request by ID for the authenticated user.
|
||||||
|
/// Also deletes all related backtests associated with this bundle request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The ID of the bundle backtest request to delete.</param>
|
||||||
|
/// <returns>An ActionResult indicating the outcome of the operation.</returns>
|
||||||
|
[HttpDelete]
|
||||||
|
[Route("Bundle/{id}")]
|
||||||
|
public async Task<ActionResult> DeleteBundleBacktestRequest(string id)
|
||||||
|
{
|
||||||
|
var user = await GetUser();
|
||||||
|
|
||||||
|
// First, delete the bundle request
|
||||||
|
_backtestRepository.DeleteBundleBacktestRequestByIdForUser(user, id);
|
||||||
|
|
||||||
|
// Then, delete all related backtests
|
||||||
|
var backtestsDeleted = _backtester.DeleteBacktestsByRequestId(id);
|
||||||
|
|
||||||
|
return Ok(new {
|
||||||
|
BundleRequestDeleted = true,
|
||||||
|
RelatedBacktestsDeleted = backtestsDeleted
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -630,6 +654,8 @@ public class BacktestController : BaseController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notifies subscribers about the backtesting results via SignalR.
|
/// Notifies subscribers about the backtesting results via SignalR.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,4 +15,12 @@ public interface IBacktestRepository
|
|||||||
void DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids);
|
void DeleteBacktestsByIdsForUser(User user, IEnumerable<string> ids);
|
||||||
void DeleteAllBacktestsForUser(User user);
|
void DeleteAllBacktestsForUser(User user);
|
||||||
void DeleteBacktestsByRequestId(string requestId);
|
void DeleteBacktestsByRequestId(string requestId);
|
||||||
|
|
||||||
|
// Bundle backtest methods
|
||||||
|
void InsertBundleBacktestRequestForUser(User user, BundleBacktestRequest bundleRequest);
|
||||||
|
IEnumerable<BundleBacktestRequest> GetBundleBacktestRequestsByUser(User user);
|
||||||
|
BundleBacktestRequest? GetBundleBacktestRequestByIdForUser(User user, string id);
|
||||||
|
void UpdateBundleBacktestRequest(BundleBacktestRequest bundleRequest);
|
||||||
|
void DeleteBundleBacktestRequestByIdForUser(User user, string id);
|
||||||
|
IEnumerable<BundleBacktestRequest> GetPendingBundleBacktestRequests();
|
||||||
}
|
}
|
||||||
156
src/Managing.Application.Workers/BundleBacktestWorker.cs
Normal file
156
src/Managing.Application.Workers/BundleBacktestWorker.cs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Managing.Application.Abstractions.Repositories;
|
||||||
|
using Managing.Application.Abstractions.Services;
|
||||||
|
using Managing.Application.Workers.Abstractions;
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Application.Workers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Worker for processing bundle backtest requests
|
||||||
|
/// </summary>
|
||||||
|
public class BundleBacktestWorker : BaseWorker<BundleBacktestWorker>
|
||||||
|
{
|
||||||
|
private readonly IBacktestRepository _backtestRepository;
|
||||||
|
private readonly IBacktester _backtester;
|
||||||
|
private static readonly WorkerType _workerType = WorkerType.BundleBacktest;
|
||||||
|
|
||||||
|
public BundleBacktestWorker(
|
||||||
|
IBacktestRepository backtestRepository,
|
||||||
|
IBacktester backtester,
|
||||||
|
ILogger<BundleBacktestWorker> logger,
|
||||||
|
IWorkerService workerService) : base(
|
||||||
|
_workerType,
|
||||||
|
logger,
|
||||||
|
TimeSpan.FromMinutes(1),
|
||||||
|
workerService)
|
||||||
|
{
|
||||||
|
_backtestRepository = backtestRepository;
|
||||||
|
_backtester = backtester;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task Run(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get pending bundle backtest requests
|
||||||
|
var pendingRequests = _backtestRepository.GetPendingBundleBacktestRequests();
|
||||||
|
|
||||||
|
foreach (var bundleRequest in pendingRequests)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
await ProcessBundleRequest(bundleRequest, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error in BundleBacktestWorker");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessBundleRequest(BundleBacktestRequest bundleRequest, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Starting to process bundle backtest request {RequestId}", bundleRequest.RequestId);
|
||||||
|
|
||||||
|
// Update status to running
|
||||||
|
bundleRequest.Status = BundleBacktestRequestStatus.Running;
|
||||||
|
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||||
|
|
||||||
|
// Deserialize the backtest requests as dynamic objects
|
||||||
|
var backtestRequests = JsonSerializer.Deserialize<List<JsonElement>>(bundleRequest.BacktestRequestsJson);
|
||||||
|
if (backtestRequests == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to deserialize backtest requests");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each backtest request
|
||||||
|
for (int i = 0; i < backtestRequests.Count; i++)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
break;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var requestElement = backtestRequests[i];
|
||||||
|
|
||||||
|
// Update current backtest being processed
|
||||||
|
bundleRequest.CurrentBacktest = $"Backtest {i + 1} of {backtestRequests.Count}";
|
||||||
|
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||||
|
|
||||||
|
// Convert JSON element to domain model and run backtest
|
||||||
|
await RunSingleBacktest(requestElement, bundleRequest.RequestId, cancellationToken);
|
||||||
|
|
||||||
|
// Update progress
|
||||||
|
bundleRequest.CompletedBacktests++;
|
||||||
|
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||||
|
|
||||||
|
_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}",
|
||||||
|
i + 1, bundleRequest.RequestId);
|
||||||
|
bundleRequest.FailedBacktests++;
|
||||||
|
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update final status
|
||||||
|
if (bundleRequest.FailedBacktests == 0)
|
||||||
|
{
|
||||||
|
bundleRequest.Status = BundleBacktestRequestStatus.Completed;
|
||||||
|
}
|
||||||
|
else if (bundleRequest.CompletedBacktests == 0)
|
||||||
|
{
|
||||||
|
bundleRequest.Status = BundleBacktestRequestStatus.Failed;
|
||||||
|
bundleRequest.ErrorMessage = "All backtests failed";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bundleRequest.Status = BundleBacktestRequestStatus.Completed;
|
||||||
|
bundleRequest.ErrorMessage = $"{bundleRequest.FailedBacktests} backtests failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
bundleRequest.CompletedAt = DateTime.UtcNow;
|
||||||
|
bundleRequest.CurrentBacktest = null;
|
||||||
|
_backtestRepository.UpdateBundleBacktestRequest(bundleRequest);
|
||||||
|
|
||||||
|
_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunSingleBacktest(JsonElement requestElement, string bundleRequestId, 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -137,6 +137,11 @@ public static class WorkersBootstrap
|
|||||||
services.AddHostedService<GeneticAlgorithmWorker>();
|
services.AddHostedService<GeneticAlgorithmWorker>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configuration.GetValue<bool>("WorkerBundleBacktest", false))
|
||||||
|
{
|
||||||
|
services.AddHostedService<BundleBacktestWorker>();
|
||||||
|
}
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -383,7 +383,8 @@ public static class Enums
|
|||||||
BotManager,
|
BotManager,
|
||||||
FundingRatesWatcher,
|
FundingRatesWatcher,
|
||||||
BalanceTracking,
|
BalanceTracking,
|
||||||
GeneticAlgorithm
|
GeneticAlgorithm,
|
||||||
|
BundleBacktest
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum WorkflowUsage
|
public enum WorkflowUsage
|
||||||
|
|||||||
146
src/Managing.Domain/Backtests/BundleBacktestRequest.cs
Normal file
146
src/Managing.Domain/Backtests/BundleBacktestRequest.cs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Managing.Domain.Users;
|
||||||
|
|
||||||
|
namespace Managing.Domain.Backtests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Domain model for bundle backtest requests
|
||||||
|
/// </summary>
|
||||||
|
public class BundleBacktestRequest
|
||||||
|
{
|
||||||
|
public BundleBacktestRequest()
|
||||||
|
{
|
||||||
|
RequestId = Guid.NewGuid().ToString();
|
||||||
|
CreatedAt = DateTime.UtcNow;
|
||||||
|
Status = BundleBacktestRequestStatus.Pending;
|
||||||
|
Results = new List<Backtest>();
|
||||||
|
BacktestRequestsJson = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor that allows setting a specific ID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requestId">The specific ID to use</param>
|
||||||
|
public BundleBacktestRequest(string requestId)
|
||||||
|
{
|
||||||
|
RequestId = requestId;
|
||||||
|
CreatedAt = DateTime.UtcNow;
|
||||||
|
Status = BundleBacktestRequestStatus.Pending;
|
||||||
|
Results = new List<Backtest>();
|
||||||
|
BacktestRequestsJson = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unique identifier for the bundle backtest request
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public string RequestId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user who created this request
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public User User { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the request was created
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the request was completed (if completed)
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? CompletedAt { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current status of the bundle backtest request
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public BundleBacktestRequestStatus Status { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of backtest requests to execute (serialized as JSON)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public string BacktestRequestsJson { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The results of the bundle backtest execution
|
||||||
|
/// </summary>
|
||||||
|
public List<Backtest> Results { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of backtests in the bundle
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public int TotalBacktests { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of backtests completed so far
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public int CompletedBacktests { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of backtests that failed
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public int FailedBacktests { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Progress percentage (0-100)
|
||||||
|
/// </summary>
|
||||||
|
public double ProgressPercentage => TotalBacktests > 0 ? (double)CompletedBacktests / TotalBacktests * 100 : 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Error message if the request failed
|
||||||
|
/// </summary>
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Progress information (JSON serialized)
|
||||||
|
/// </summary>
|
||||||
|
public string? ProgressInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current backtest being processed
|
||||||
|
/// </summary>
|
||||||
|
public string? CurrentBacktest { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Estimated time remaining in seconds
|
||||||
|
/// </summary>
|
||||||
|
public int? EstimatedTimeRemainingSeconds { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Status of a bundle backtest request
|
||||||
|
/// </summary>
|
||||||
|
public enum BundleBacktestRequestStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Request is pending execution
|
||||||
|
/// </summary>
|
||||||
|
Pending,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request is currently being processed
|
||||||
|
/// </summary>
|
||||||
|
Running,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request completed successfully
|
||||||
|
/// </summary>
|
||||||
|
Completed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request failed with an error
|
||||||
|
/// </summary>
|
||||||
|
Failed,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request was cancelled
|
||||||
|
/// </summary>
|
||||||
|
Cancelled
|
||||||
|
}
|
||||||
@@ -12,10 +12,14 @@ namespace Managing.Infrastructure.Databases;
|
|||||||
public class BacktestRepository : IBacktestRepository
|
public class BacktestRepository : IBacktestRepository
|
||||||
{
|
{
|
||||||
private readonly IMongoRepository<BacktestDto> _backtestRepository;
|
private readonly IMongoRepository<BacktestDto> _backtestRepository;
|
||||||
|
private readonly IMongoRepository<BundleBacktestRequestDto> _bundleBacktestRepository;
|
||||||
|
|
||||||
public BacktestRepository(IMongoRepository<BacktestDto> backtestRepository)
|
public BacktestRepository(
|
||||||
|
IMongoRepository<BacktestDto> backtestRepository,
|
||||||
|
IMongoRepository<BundleBacktestRequestDto> bundleBacktestRepository)
|
||||||
{
|
{
|
||||||
_backtestRepository = backtestRepository;
|
_backtestRepository = backtestRepository;
|
||||||
|
_bundleBacktestRepository = bundleBacktestRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User-specific operations
|
// User-specific operations
|
||||||
@@ -305,4 +309,107 @@ public class BacktestRepository : IBacktestRepository
|
|||||||
|
|
||||||
return (mappedBacktests, (int)totalCount);
|
return (mappedBacktests, (int)totalCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bundle backtest methods
|
||||||
|
public void InsertBundleBacktestRequestForUser(User user, BundleBacktestRequest bundleRequest)
|
||||||
|
{
|
||||||
|
bundleRequest.User = user;
|
||||||
|
var dto = MapToDto(bundleRequest);
|
||||||
|
_bundleBacktestRepository.InsertOne(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<BundleBacktestRequest> GetBundleBacktestRequestsByUser(User user)
|
||||||
|
{
|
||||||
|
var bundleRequests = _bundleBacktestRepository.AsQueryable()
|
||||||
|
.Where(b => b.User.Name == user.Name)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return bundleRequests.Select(MapToDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BundleBacktestRequest? GetBundleBacktestRequestByIdForUser(User user, string id)
|
||||||
|
{
|
||||||
|
var bundleRequest = _bundleBacktestRepository.FindOne(b => b.RequestId == id);
|
||||||
|
|
||||||
|
if (bundleRequest != null && bundleRequest.User.Name == user.Name)
|
||||||
|
{
|
||||||
|
return MapToDomain(bundleRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateBundleBacktestRequest(BundleBacktestRequest bundleRequest)
|
||||||
|
{
|
||||||
|
var dto = MapToDto(bundleRequest);
|
||||||
|
_bundleBacktestRepository.ReplaceOne(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteBundleBacktestRequestByIdForUser(User user, string id)
|
||||||
|
{
|
||||||
|
var bundleRequest = _bundleBacktestRepository.FindOne(b => b.RequestId == id);
|
||||||
|
|
||||||
|
if (bundleRequest != null && bundleRequest.User.Name == user.Name)
|
||||||
|
{
|
||||||
|
_bundleBacktestRepository.DeleteById(bundleRequest.Id.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<BundleBacktestRequest> GetPendingBundleBacktestRequests()
|
||||||
|
{
|
||||||
|
var pendingRequests = _bundleBacktestRepository.AsQueryable()
|
||||||
|
.Where(b => b.Status == BundleBacktestRequestStatus.Pending)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return pendingRequests.Select(MapToDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a domain model to DTO
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="domain">The domain model</param>
|
||||||
|
/// <returns>The DTO</returns>
|
||||||
|
private static BundleBacktestRequestDto MapToDto(BundleBacktestRequest domain)
|
||||||
|
{
|
||||||
|
return new BundleBacktestRequestDto
|
||||||
|
{
|
||||||
|
RequestId = domain.RequestId,
|
||||||
|
User = MongoMappers.Map(domain.User),
|
||||||
|
CompletedAt = domain.CompletedAt,
|
||||||
|
Status = domain.Status,
|
||||||
|
BacktestRequestsJson = domain.BacktestRequestsJson,
|
||||||
|
TotalBacktests = domain.TotalBacktests,
|
||||||
|
CompletedBacktests = domain.CompletedBacktests,
|
||||||
|
FailedBacktests = domain.FailedBacktests,
|
||||||
|
ErrorMessage = domain.ErrorMessage,
|
||||||
|
ProgressInfo = domain.ProgressInfo,
|
||||||
|
CurrentBacktest = domain.CurrentBacktest,
|
||||||
|
EstimatedTimeRemainingSeconds = domain.EstimatedTimeRemainingSeconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a DTO to domain model
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto">The DTO</param>
|
||||||
|
/// <returns>The domain model</returns>
|
||||||
|
private static BundleBacktestRequest MapToDomain(BundleBacktestRequestDto dto)
|
||||||
|
{
|
||||||
|
return new BundleBacktestRequest
|
||||||
|
{
|
||||||
|
RequestId = dto.RequestId,
|
||||||
|
User = MongoMappers.Map(dto.User),
|
||||||
|
CreatedAt = dto.CreatedAt,
|
||||||
|
CompletedAt = dto.CompletedAt,
|
||||||
|
Status = dto.Status,
|
||||||
|
BacktestRequestsJson = dto.BacktestRequestsJson,
|
||||||
|
TotalBacktests = dto.TotalBacktests,
|
||||||
|
CompletedBacktests = dto.CompletedBacktests,
|
||||||
|
FailedBacktests = dto.FailedBacktests,
|
||||||
|
ErrorMessage = dto.ErrorMessage,
|
||||||
|
ProgressInfo = dto.ProgressInfo,
|
||||||
|
CurrentBacktest = dto.CurrentBacktest,
|
||||||
|
EstimatedTimeRemainingSeconds = dto.EstimatedTimeRemainingSeconds
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Managing.Domain.Backtests;
|
||||||
|
using Managing.Infrastructure.Databases.MongoDb.Attributes;
|
||||||
|
using Managing.Infrastructure.Databases.MongoDb.Configurations;
|
||||||
|
|
||||||
|
namespace Managing.Infrastructure.Databases.MongoDb.Collections;
|
||||||
|
|
||||||
|
[BsonCollection("BundleBacktestRequests")]
|
||||||
|
public class BundleBacktestRequestDto : Document
|
||||||
|
{
|
||||||
|
public string RequestId { get; set; } = string.Empty;
|
||||||
|
public UserDto User { get; set; } = new();
|
||||||
|
public DateTime? CompletedAt { get; set; }
|
||||||
|
public BundleBacktestRequestStatus Status { get; set; }
|
||||||
|
public string BacktestRequestsJson { get; set; } = string.Empty;
|
||||||
|
public int TotalBacktests { get; set; }
|
||||||
|
public int CompletedBacktests { get; set; }
|
||||||
|
public int FailedBacktests { get; set; }
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
public string? ProgressInfo { get; set; }
|
||||||
|
public string? CurrentBacktest { get; set; }
|
||||||
|
public int? EstimatedTimeRemainingSeconds { get; set; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user