Add bundle backtest
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
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;
|
||||
@@ -12,6 +11,7 @@ using Managing.Domain.Strategies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using MoneyManagementRequest = Managing.Domain.Backtests.MoneyManagementRequest;
|
||||
|
||||
namespace Managing.Api.Controllers;
|
||||
|
||||
@@ -33,7 +33,6 @@ 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.
|
||||
@@ -52,7 +51,6 @@ public class BacktestController : BaseController
|
||||
IAccountService accountService,
|
||||
IMoneyManagementService moneyManagementService,
|
||||
IGeneticService geneticService,
|
||||
IBacktestRepository backtestRepository,
|
||||
IUserService userService) : base(userService)
|
||||
{
|
||||
_hubContext = hubContext;
|
||||
@@ -61,7 +59,6 @@ public class BacktestController : BaseController
|
||||
_accountService = accountService;
|
||||
_moneyManagementService = moneyManagementService;
|
||||
_geneticService = geneticService;
|
||||
_backtestRepository = backtestRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -153,8 +150,8 @@ public class BacktestController : BaseController
|
||||
[HttpGet]
|
||||
[Route("ByRequestId/{requestId}/Paginated")]
|
||||
public async Task<ActionResult<PaginatedBacktestsResponse>> GetBacktestsByRequestIdPaginated(
|
||||
string requestId,
|
||||
int page = 1,
|
||||
string requestId,
|
||||
int page = 1,
|
||||
int pageSize = 50,
|
||||
string sortBy = "score",
|
||||
string sortOrder = "desc")
|
||||
@@ -179,10 +176,11 @@ public class BacktestController : BaseController
|
||||
return BadRequest("Sort order must be 'asc' or 'desc'");
|
||||
}
|
||||
|
||||
var (backtests, totalCount) = _backtester.GetBacktestsByRequestIdPaginated(requestId, page, pageSize, sortBy, sortOrder);
|
||||
|
||||
var (backtests, totalCount) =
|
||||
_backtester.GetBacktestsByRequestIdPaginated(requestId, page, pageSize, sortBy, sortOrder);
|
||||
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
||||
|
||||
var response = new PaginatedBacktestsResponse
|
||||
{
|
||||
Backtests = backtests.Select(b => new LightBacktestResponse
|
||||
@@ -410,10 +408,12 @@ public class BacktestController : BaseController
|
||||
/// 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>
|
||||
/// <param name="name">Display name for the bundle (required).</param>
|
||||
/// <returns>The bundle backtest request with ID for tracking progress.</returns>
|
||||
[HttpPost]
|
||||
[Route("Bundle")]
|
||||
public async Task<ActionResult<BundleBacktestRequest>> RunBundle([FromBody] List<RunBacktestRequest> requests)
|
||||
public async Task<ActionResult<BundleBacktestRequest>> RunBundle([FromBody] List<RunBacktestRequest> requests,
|
||||
[FromQuery] string name)
|
||||
{
|
||||
if (requests == null || !requests.Any())
|
||||
{
|
||||
@@ -425,10 +425,15 @@ public class BacktestController : BaseController
|
||||
return BadRequest("Maximum of 10 backtests allowed per bundle request");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return BadRequest("Bundle name is required");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var user = await GetUser();
|
||||
|
||||
|
||||
// Validate all requests before creating the bundle
|
||||
foreach (var request in requests)
|
||||
{
|
||||
@@ -449,7 +454,8 @@ public class BacktestController : BaseController
|
||||
|
||||
if (string.IsNullOrEmpty(request.Config.MoneyManagementName) && request.Config.MoneyManagement == null)
|
||||
{
|
||||
return BadRequest("Invalid request: Either money management name or money management object is required");
|
||||
return BadRequest(
|
||||
"Invalid request: Either money management name or money management object is required");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,10 +467,11 @@ public class BacktestController : BaseController
|
||||
TotalBacktests = requests.Count,
|
||||
CompletedBacktests = 0,
|
||||
FailedBacktests = 0,
|
||||
Status = BundleBacktestRequestStatus.Pending
|
||||
Status = BundleBacktestRequestStatus.Pending,
|
||||
Name = name
|
||||
};
|
||||
|
||||
_backtestRepository.InsertBundleBacktestRequestForUser(user, bundleRequest);
|
||||
_backtester.InsertBundleBacktestRequestForUser(user, bundleRequest);
|
||||
|
||||
return Ok(bundleRequest);
|
||||
}
|
||||
@@ -483,7 +490,7 @@ public class BacktestController : BaseController
|
||||
public async Task<ActionResult<IEnumerable<BundleBacktestRequest>>> GetBundleBacktestRequests()
|
||||
{
|
||||
var user = await GetUser();
|
||||
var bundleRequests = _backtestRepository.GetBundleBacktestRequestsByUser(user);
|
||||
var bundleRequests = _backtester.GetBundleBacktestRequestsByUser(user);
|
||||
return Ok(bundleRequests);
|
||||
}
|
||||
|
||||
@@ -497,7 +504,7 @@ public class BacktestController : BaseController
|
||||
public async Task<ActionResult<BundleBacktestRequest>> GetBundleBacktestRequest(string id)
|
||||
{
|
||||
var user = await GetUser();
|
||||
var bundleRequest = _backtestRepository.GetBundleBacktestRequestByIdForUser(user, id);
|
||||
var bundleRequest = _backtester.GetBundleBacktestRequestByIdForUser(user, id);
|
||||
|
||||
if (bundleRequest == null)
|
||||
{
|
||||
@@ -518,16 +525,17 @@ public class BacktestController : BaseController
|
||||
public async Task<ActionResult> DeleteBundleBacktestRequest(string id)
|
||||
{
|
||||
var user = await GetUser();
|
||||
|
||||
|
||||
// First, delete the bundle request
|
||||
_backtestRepository.DeleteBundleBacktestRequestByIdForUser(user, id);
|
||||
|
||||
_backtester.DeleteBundleBacktestRequestByIdForUser(user, id);
|
||||
|
||||
// Then, delete all related backtests
|
||||
var backtestsDeleted = _backtester.DeleteBacktestsByRequestId(id);
|
||||
|
||||
return Ok(new {
|
||||
BundleRequestDeleted = true,
|
||||
RelatedBacktestsDeleted = backtestsDeleted
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
BundleRequestDeleted = true,
|
||||
RelatedBacktestsDeleted = backtestsDeleted
|
||||
});
|
||||
}
|
||||
|
||||
@@ -641,21 +649,21 @@ public class BacktestController : BaseController
|
||||
public async Task<ActionResult> DeleteGeneticRequest(string id)
|
||||
{
|
||||
var user = await GetUser();
|
||||
|
||||
|
||||
// First, delete the genetic request
|
||||
_geneticService.DeleteGeneticRequestByIdForUser(user, id);
|
||||
|
||||
|
||||
// Then, delete all related backtests
|
||||
var backtestsDeleted = _backtester.DeleteBacktestsByRequestId(id);
|
||||
|
||||
return Ok(new {
|
||||
GeneticRequestDeleted = true,
|
||||
RelatedBacktestsDeleted = backtestsDeleted
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
GeneticRequestDeleted = true,
|
||||
RelatedBacktestsDeleted = backtestsDeleted
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Notifies subscribers about the backtesting results via SignalR.
|
||||
/// </summary>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Hubs;
|
||||
using Managing.Application.ManageBot.Commands;
|
||||
using Managing.Common;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
|
||||
@@ -4,6 +4,7 @@ using Managing.Application.Abstractions;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Hubs;
|
||||
using Managing.Application.ManageBot.Commands;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Scenarios;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Managing.Domain.Backtests;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Api.Models.Requests;
|
||||
@@ -7,8 +8,6 @@ namespace Managing.Api.Models.Requests;
|
||||
/// </summary>
|
||||
public class GetCandlesWithIndicatorsRequest
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The ticker symbol.
|
||||
/// </summary>
|
||||
@@ -33,4 +32,4 @@ public class GetCandlesWithIndicatorsRequest
|
||||
/// Optional scenario for calculating indicators.
|
||||
/// </summary>
|
||||
public ScenarioRequest Scenario { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Api.Models.Requests;
|
||||
|
||||
/// <summary>
|
||||
/// Request model for indicator configuration without user information
|
||||
/// </summary>
|
||||
public class IndicatorRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the indicator
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of indicator
|
||||
/// </summary>
|
||||
[Required]
|
||||
public IndicatorType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The signal type for this indicator
|
||||
/// </summary>
|
||||
[Required]
|
||||
public SignalType SignalType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Minimum history required for this indicator
|
||||
/// </summary>
|
||||
public int MinimumHistory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Period parameter for the indicator
|
||||
/// </summary>
|
||||
public int? Period { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fast periods parameter for indicators like MACD
|
||||
/// </summary>
|
||||
public int? FastPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Slow periods parameter for indicators like MACD
|
||||
/// </summary>
|
||||
public int? SlowPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Signal periods parameter for indicators like MACD
|
||||
/// </summary>
|
||||
public int? SignalPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier parameter for indicators like SuperTrend
|
||||
/// </summary>
|
||||
public double? Multiplier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Smooth periods parameter
|
||||
/// </summary>
|
||||
public int? SmoothPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stochastic periods parameter
|
||||
/// </summary>
|
||||
public int? StochPeriods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cycle periods parameter
|
||||
/// </summary>
|
||||
public int? CyclePeriods { get; set; }
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Common;
|
||||
|
||||
namespace Managing.Api.Models.Requests;
|
||||
|
||||
public class MoneyManagementRequest
|
||||
{
|
||||
[Required] public string Name { get; set; }
|
||||
[Required] public Enums.Timeframe Timeframe { get; set; }
|
||||
[Required] public decimal StopLoss { get; set; }
|
||||
[Required] public decimal TakeProfit { get; set; }
|
||||
[Required] public decimal Leverage { get; set; }
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using Managing.Domain.MoneyManagements;
|
||||
|
||||
namespace Managing.Api.Models.Requests;
|
||||
|
||||
/// <summary>
|
||||
/// Request model for running a backtest
|
||||
/// </summary>
|
||||
public class RunBacktestRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The trading bot configuration request to use for the backtest
|
||||
/// </summary>
|
||||
public TradingBotConfigRequest Config { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start date for the backtest
|
||||
/// </summary>
|
||||
public DateTime StartDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end date for the backtest
|
||||
/// </summary>
|
||||
public DateTime EndDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The starting balance for the backtest
|
||||
/// </summary>
|
||||
public decimal Balance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to only watch the backtest without executing trades
|
||||
/// </summary>
|
||||
public bool WatchOnly { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to save the backtest results
|
||||
/// </summary>
|
||||
public bool Save { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the money management to use (optional if MoneyManagement is provided)
|
||||
/// </summary>
|
||||
public string? MoneyManagementName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The money management details (optional if MoneyManagementName is provided)
|
||||
/// </summary>
|
||||
public MoneyManagement? MoneyManagement { get; set; }
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Managing.Api.Models.Requests;
|
||||
|
||||
/// <summary>
|
||||
/// Request model for scenario configuration without user information
|
||||
/// </summary>
|
||||
public class ScenarioRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the scenario
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of indicator configurations for this scenario
|
||||
/// </summary>
|
||||
[Required]
|
||||
public List<IndicatorRequest> Indicators { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// The loopback period for the scenario
|
||||
/// </summary>
|
||||
public int? LoopbackPeriod { get; set; }
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Domain.Bots;
|
||||
|
||||
namespace Managing.Api.Models.Requests
|
||||
{
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Api.Models.Requests;
|
||||
|
||||
/// <summary>
|
||||
/// Simplified trading bot configuration request with only primary properties
|
||||
/// </summary>
|
||||
public class TradingBotConfigRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The account name to use for trading
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string AccountName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ticker/symbol to trade
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Ticker Ticker { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The timeframe for trading decisions
|
||||
/// </summary>
|
||||
[Required]
|
||||
public Timeframe Timeframe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this bot is for watching only (no actual trading)
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool IsForWatchingOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The initial trading balance for the bot
|
||||
/// </summary>
|
||||
[Required]
|
||||
public decimal BotTradingBalance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name/identifier for this bot
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required] public bool FlipPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cooldown period between trades (in candles)
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int CooldownPeriod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum consecutive losses before stopping the bot
|
||||
/// </summary>
|
||||
[Required]
|
||||
public int MaxLossStreak { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scenario configuration (takes precedence over ScenarioName)
|
||||
/// </summary>
|
||||
public ScenarioRequest? Scenario { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scenario name to load from database (only used when Scenario is not provided)
|
||||
/// </summary>
|
||||
public string? ScenarioName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The money management name to load from database (only used when MoneyManagement is not provided)
|
||||
/// </summary>
|
||||
public string? MoneyManagementName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The money management object to use for the bot
|
||||
/// </summary>
|
||||
public MoneyManagementRequest? MoneyManagement { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum time in hours that a position can remain open before being automatically closed
|
||||
/// </summary>
|
||||
public decimal? MaxPositionTimeHours { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to close positions early when they become profitable
|
||||
/// </summary>
|
||||
public bool CloseEarlyWhenProfitable { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to only flip positions when the current position is in profit
|
||||
/// </summary>
|
||||
public bool FlipOnlyWhenInProfit { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use Synth API for predictions and risk assessment
|
||||
/// </summary>
|
||||
public bool UseSynthApi { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use Synth predictions for position sizing adjustments
|
||||
/// </summary>
|
||||
public bool UseForPositionSizing { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use Synth predictions for signal filtering
|
||||
/// </summary>
|
||||
public bool UseForSignalFiltering { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use Synth predictions for dynamic stop-loss/take-profit adjustments
|
||||
/// </summary>
|
||||
public bool UseForDynamicStopLoss { get; set; } = true;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
|
||||
namespace Managing.Api.Models.Requests;
|
||||
|
||||
Reference in New Issue
Block a user