Deserialized variant for bundle backtest

This commit is contained in:
2025-10-23 12:31:30 +07:00
parent a1fe7ed3b3
commit 6bfefc91c8
5 changed files with 231 additions and 88 deletions

View File

@@ -1,5 +1,6 @@
using System.Text.Json;
using Managing.Api.Models.Requests;
using Managing.Api.Models.Responses;
using Managing.Application.Abstractions.Services;
using Managing.Application.Abstractions.Shared;
using Managing.Application.Hubs;
@@ -677,75 +678,6 @@ public class BacktestController : BaseController
}
}
/// <summary>
/// Generates individual backtest requests from variant configuration
/// </summary>
/// <param name="request">The bundle backtest request</param>
/// <param name="accountName">The account name to use for all backtests</param>
/// <returns>List of individual backtest requests</returns>
private List<RunBacktestRequest> GenerateBacktestRequests(RunBundleBacktestRequest request, string accountName)
{
var backtestRequests = new List<RunBacktestRequest>();
foreach (var dateRange in request.DateTimeRanges)
{
foreach (var mmVariant in request.MoneyManagementVariants)
{
foreach (var ticker in request.TickerVariants)
{
var config = new TradingBotConfigRequest
{
AccountName = accountName,
Ticker = ticker,
Timeframe = request.UniversalConfig.Timeframe,
IsForWatchingOnly = request.UniversalConfig.IsForWatchingOnly,
BotTradingBalance = request.UniversalConfig.BotTradingBalance,
Name =
$"{request.UniversalConfig.BotName}_{ticker}_{dateRange.StartDate:yyyyMMdd}_{dateRange.EndDate:yyyyMMdd}",
FlipPosition = request.UniversalConfig.FlipPosition,
CooldownPeriod = request.UniversalConfig.CooldownPeriod,
MaxLossStreak = request.UniversalConfig.MaxLossStreak,
Scenario = request.UniversalConfig.Scenario,
ScenarioName = request.UniversalConfig.ScenarioName,
MoneyManagement = mmVariant.MoneyManagement,
MaxPositionTimeHours = request.UniversalConfig.MaxPositionTimeHours,
CloseEarlyWhenProfitable = request.UniversalConfig.CloseEarlyWhenProfitable,
FlipOnlyWhenInProfit = request.UniversalConfig.FlipOnlyWhenInProfit,
UseSynthApi = request.UniversalConfig.UseSynthApi,
UseForPositionSizing = request.UniversalConfig.UseForPositionSizing,
UseForSignalFiltering = request.UniversalConfig.UseForSignalFiltering,
UseForDynamicStopLoss = request.UniversalConfig.UseForDynamicStopLoss
};
var backtestRequest = new RunBacktestRequest
{
Config = config,
StartDate = dateRange.StartDate,
EndDate = dateRange.EndDate,
Balance = request.UniversalConfig.BotTradingBalance,
WatchOnly = request.UniversalConfig.WatchOnly,
Save = request.UniversalConfig.Save,
WithCandles = request.UniversalConfig.WithCandles,
MoneyManagement = mmVariant.MoneyManagement != null
? new MoneyManagement
{
Name = mmVariant.MoneyManagement.Name,
Timeframe = mmVariant.MoneyManagement.Timeframe,
StopLoss = mmVariant.MoneyManagement.StopLoss,
TakeProfit = mmVariant.MoneyManagement.TakeProfit,
Leverage = mmVariant.MoneyManagement.Leverage
}
: null
};
backtestRequests.Add(backtestRequest);
}
}
}
return backtestRequests;
}
/// <summary>
/// Retrieves all bundle backtest requests for the authenticated user.
/// </summary>
@@ -781,7 +713,8 @@ public class BacktestController : BaseController
return NotFound($"Bundle backtest request with ID {id} not found or doesn't belong to the current user.");
}
return Ok(bundleRequest);
var viewModel = BundleBacktestRequestViewModel.FromDomain(bundleRequest);
return Ok(viewModel);
}
/// <summary>

View File

@@ -0,0 +1,170 @@
#nullable enable
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using Managing.Domain.Backtests;
using static Managing.Common.Enums;
namespace Managing.Api.Models.Responses;
/// <summary>
/// View model for bundle backtest requests with deserialized variant lists for frontend consumption
/// </summary>
public class BundleBacktestRequestViewModel
{
/// <summary>
/// Unique identifier for the bundle backtest request
/// </summary>
[Required]
public Guid RequestId { 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>
/// Display name for the bundle backtest request
/// </summary>
[Required]
public string Name { get; set; } = string.Empty;
/// <summary>
/// The universal configuration that applies to all backtests
/// </summary>
[Required]
public BundleBacktestUniversalConfig UniversalConfig { get; set; } = new();
/// <summary>
/// The list of DateTime ranges to test
/// </summary>
[Required]
public List<DateTimeRange> DateTimeRanges { get; set; } = new();
/// <summary>
/// The list of money management variants to test
/// </summary>
[Required]
public List<MoneyManagementVariant> MoneyManagementVariants { get; set; } = new();
/// <summary>
/// The list of ticker variants to test
/// </summary>
[Required]
public List<Ticker> TickerVariants { get; set; } = new();
/// <summary>
/// The results of the bundle backtest execution
/// </summary>
public List<string> 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>
/// Maps a domain model to a view model by deserializing JSON fields
/// </summary>
/// <param name="request">The domain bundle backtest request</param>
/// <returns>A view model with deserialized lists</returns>
public static BundleBacktestRequestViewModel FromDomain(BundleBacktestRequest request)
{
var viewModel = new BundleBacktestRequestViewModel
{
RequestId = request.RequestId,
CreatedAt = request.CreatedAt,
CompletedAt = request.CompletedAt,
Status = request.Status,
Name = request.Name,
Results = request.Results,
TotalBacktests = request.TotalBacktests,
CompletedBacktests = request.CompletedBacktests,
FailedBacktests = request.FailedBacktests,
ErrorMessage = request.ErrorMessage,
ProgressInfo = request.ProgressInfo,
CurrentBacktest = request.CurrentBacktest,
EstimatedTimeRemainingSeconds = request.EstimatedTimeRemainingSeconds
};
// Deserialize UniversalConfig
if (!string.IsNullOrEmpty(request.UniversalConfigJson))
{
viewModel.UniversalConfig = JsonSerializer.Deserialize<BundleBacktestUniversalConfig>(request.UniversalConfigJson)
?? new BundleBacktestUniversalConfig();
}
// Deserialize DateTimeRanges
if (!string.IsNullOrEmpty(request.DateTimeRangesJson))
{
viewModel.DateTimeRanges = JsonSerializer.Deserialize<List<DateTimeRange>>(request.DateTimeRangesJson)
?? new List<DateTimeRange>();
}
// Deserialize MoneyManagementVariants
if (!string.IsNullOrEmpty(request.MoneyManagementVariantsJson))
{
viewModel.MoneyManagementVariants = JsonSerializer.Deserialize<List<MoneyManagementVariant>>(request.MoneyManagementVariantsJson)
?? new List<MoneyManagementVariant>();
}
// Deserialize TickerVariants
if (!string.IsNullOrEmpty(request.TickerVariantsJson))
{
viewModel.TickerVariants = JsonSerializer.Deserialize<List<Ticker>>(request.TickerVariantsJson)
?? new List<Ticker>();
}
return viewModel;
}
}