Setup bundle for backtest

This commit is contained in:
2025-07-21 00:01:13 +07:00
parent 4c07d7323f
commit 0870edee61
8 changed files with 543 additions and 72 deletions

View File

@@ -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.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Domain.Backtests;
@@ -31,6 +33,7 @@ public class BacktestController : BaseController
private readonly IAccountService _accountService;
private readonly IMoneyManagementService _moneyManagementService;
private readonly IGeneticService _geneticService;
private readonly IBacktestRepository _backtestRepository;
/// <summary>
/// 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="moneyManagementService">The service for money management strategies.</param>
/// <param name="geneticService">The service for genetic algorithm operations.</param>
/// <param name="backtestRepository">The repository for backtest operations.</param>
public BacktestController(
IHubContext<BotHub> hubContext,
IBacktester backtester,
@@ -48,6 +52,7 @@ public class BacktestController : BaseController
IAccountService accountService,
IMoneyManagementService moneyManagementService,
IGeneticService geneticService,
IBacktestRepository backtestRepository,
IUserService userService) : base(userService)
{
_hubContext = hubContext;
@@ -56,6 +61,7 @@ public class BacktestController : BaseController
_accountService = accountService;
_moneyManagementService = moneyManagementService;
_geneticService = geneticService;
_backtestRepository = backtestRepository;
}
/// <summary>
@@ -400,14 +406,14 @@ public class BacktestController : BaseController
}
/// <summary>
/// Runs multiple backtests in a bundle with the specified configurations.
/// This endpoint receives a list of backtest requests and will execute them all.
/// Creates a bundle backtest request with the specified configurations.
/// This endpoint creates a request that will be processed by a background worker.
/// </summary>
/// <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]
[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())
{
@@ -419,92 +425,110 @@ public class BacktestController : BaseController
return BadRequest("Maximum of 10 backtests allowed per bundle request");
}
var user = await GetUser();
var results = new List<object>();
foreach (var request in requests)
try
{
try
var user = await GetUser();
// Validate all requests before creating the bundle
foreach (var request in requests)
{
// Validate individual request
if (request?.Config == null)
{
results.Add(new
{
Id = Guid.NewGuid().ToString(),
Status = "Error",
Message = "Invalid request: Configuration is required"
});
continue;
return BadRequest("Invalid request: Configuration is required");
}
if (string.IsNullOrEmpty(request.Config.AccountName))
{
results.Add(new
{
Id = Guid.NewGuid().ToString(),
Status = "Error",
Message = "Invalid request: Account name is required"
});
continue;
return BadRequest("Invalid request: Account name is required");
}
if (string.IsNullOrEmpty(request.Config.ScenarioName) && request.Config.Scenario == null)
{
results.Add(new
{
Id = Guid.NewGuid().ToString(),
Status = "Error",
Message = "Invalid request: Either scenario name or scenario object is required"
});
continue;
return BadRequest("Invalid request: Either scenario name or scenario object is required");
}
if (string.IsNullOrEmpty(request.Config.MoneyManagementName) && request.Config.MoneyManagement == null)
{
results.Add(new
{
Id = Guid.NewGuid().ToString(),
Status = "Error",
Message = "Invalid request: Either money management name or money management object is required"
});
continue;
return BadRequest("Invalid request: Either money management name or money management object is required");
}
// 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
{
Id = Guid.NewGuid().ToString(),
Status = "Error",
Message = $"Error processing backtest: {ex.Message}"
});
}
User = user,
BacktestRequestsJson = JsonSerializer.Serialize(requests),
TotalBacktests = requests.Count,
CompletedBacktests = 0,
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>
@@ -630,6 +654,8 @@ public class BacktestController : BaseController
});
}
/// <summary>
/// Notifies subscribers about the backtesting results via SignalR.
/// </summary>