Add new parameters

This commit is contained in:
2025-06-03 01:01:17 +07:00
parent 71bcaea76d
commit 8c2e9b59de
23 changed files with 1346 additions and 510 deletions

View File

@@ -2,6 +2,7 @@
using Managing.Application.Abstractions.Services;
using Managing.Application.Hubs;
using Managing.Domain.Backtests;
using Managing.Domain.Bots;
using Managing.Domain.MoneyManagements;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -13,6 +14,7 @@ namespace Managing.Api.Controllers;
/// <summary>
/// Controller for managing backtest operations.
/// Provides endpoints for creating, retrieving, and deleting backtests.
/// Returns complete backtest configurations for easy bot deployment.
/// Requires authorization for access.
/// </summary>
[ApiController]
@@ -52,8 +54,9 @@ public class BacktestController : BaseController
/// <summary>
/// Retrieves all backtests for the authenticated user.
/// Each backtest includes the complete TradingBotConfig for easy bot deployment.
/// </summary>
/// <returns>A list of backtests.</returns>
/// <returns>A list of backtests with complete configurations.</returns>
[HttpGet]
public async Task<ActionResult<IEnumerable<Backtest>>> Backtests()
{
@@ -63,10 +66,11 @@ public class BacktestController : BaseController
/// <summary>
/// Retrieves a specific backtest by ID for the authenticated user.
/// This endpoint will also populate the candles for visualization.
/// This endpoint will also populate the candles for visualization and includes
/// the complete TradingBotConfig that can be used to start a new bot.
/// </summary>
/// <param name="id">The ID of the backtest to retrieve.</param>
/// <returns>The requested backtest with populated candle data.</returns>
/// <returns>The requested backtest with populated candle data and complete configuration.</returns>
[HttpGet("{id}")]
public async Task<ActionResult<Backtest>> Backtest(string id)
{
@@ -94,115 +98,130 @@ public class BacktestController : BaseController
}
/// <summary>
/// Runs a backtest with the specified parameters.
/// Runs a backtest with the specified configuration.
/// The returned backtest includes a complete TradingBotConfig that preserves all
/// settings including nullable MaxPositionTimeHours for easy bot deployment.
/// </summary>
/// <param name="accountName">The name of the account to use for the backtest.</param>
/// <param name="botType">The type of bot to use for the backtest.</param>
/// <param name="ticker">The ticker symbol to backtest.</param>
/// <param name="scenarioName">The name of the scenario to use for the backtest.</param>
/// <param name="timeframe">The timeframe for the backtest.</param>
/// <param name="watchOnly">Whether to only watch the backtest without executing trades.</param>
/// <param name="startDate">The start date for the backtest.</param>
/// <param name="endDate">The end date for the backtest.</param>
/// <param name="balance">The starting balance for the backtest.</param>
/// <param name="moneyManagementName">The name of the money management strategy to use.</param>
/// <param name="moneyManagement">The money management strategy details, if not using a named strategy.</param>
/// <param name="save">Whether to save the backtest results.</param>
/// <param name="cooldownPeriod">The cooldown period for the backtest.</param>
/// <param name="maxLossStreak">The maximum loss streak for the backtest.</param>
/// <returns>The result of the backtest.</returns>
/// <param name="request">The backtest request containing configuration and parameters.</param>
/// <returns>The result of the backtest with complete configuration.</returns>
[HttpPost]
[Route("Run")]
public async Task<ActionResult<Backtest>> Run(string accountName,
BotType botType,
Ticker ticker,
string scenarioName,
Timeframe timeframe,
bool watchOnly,
decimal balance,
string moneyManagementName,
DateTime startDate,
DateTime endDate,
MoneyManagement? moneyManagement = null,
bool save = false,
int cooldownPeriod = 1,
int maxLossStreak = 0)
public async Task<ActionResult<Backtest>> Run([FromBody] RunBacktestRequest request)
{
if (string.IsNullOrEmpty(accountName))
if (request?.Config == null)
{
throw new ArgumentException($"'{nameof(accountName)}' cannot be null or empty.", nameof(accountName));
return BadRequest("Backtest configuration is required");
}
if (string.IsNullOrEmpty(scenarioName))
if (string.IsNullOrEmpty(request.Config.AccountName))
{
throw new ArgumentException($"'{nameof(scenarioName)}' cannot be null or empty.", nameof(scenarioName));
return BadRequest("Account name is required");
}
if (string.IsNullOrEmpty(moneyManagementName) && moneyManagement == null)
if (string.IsNullOrEmpty(request.Config.ScenarioName))
{
throw new ArgumentException(
$"'{nameof(moneyManagementName)}' and '{nameof(moneyManagement)}' cannot be null or empty.",
nameof(moneyManagementName));
return BadRequest("Scenario name is required");
}
Backtest backtestResult = null;
var scenario = _scenarioService.GetScenario(scenarioName);
var account = await _accountService.GetAccount(accountName, true, false);
var user = await GetUser();
if (!string.IsNullOrEmpty(moneyManagementName) && moneyManagement is null)
if (string.IsNullOrEmpty(request.MoneyManagementName) && request.MoneyManagement == null)
{
moneyManagement = await _moneyManagementService.GetMoneyMangement(user, moneyManagementName);
}
else
{
moneyManagement.FormatPercentage();
return BadRequest("Either money management name or money management object is required");
}
if (scenario == null)
return BadRequest("No scenario found");
switch (botType)
try
{
case BotType.SimpleBot:
break;
case BotType.ScalpingBot:
backtestResult = await _backtester.RunScalpingBotBacktest(
account,
moneyManagement,
ticker,
scenario,
timeframe,
balance,
startDate,
endDate,
user,
watchOnly,
save,
cooldownPeriod: cooldownPeriod,
maxLossStreak: maxLossStreak);
break;
case BotType.FlippingBot:
backtestResult = await _backtester.RunFlippingBotBacktest(
account,
moneyManagement,
ticker,
scenario,
timeframe,
balance,
startDate,
endDate,
user,
watchOnly,
save,
cooldownPeriod: cooldownPeriod,
maxLossStreak: maxLossStreak);
break;
Backtest backtestResult = null;
var scenario = _scenarioService.GetScenario(request.Config.ScenarioName);
var account = await _accountService.GetAccount(request.Config.AccountName, true, false);
var user = await GetUser();
if (scenario == null)
return BadRequest("No scenario found");
// Get money management
MoneyManagement moneyManagement;
if (!string.IsNullOrEmpty(request.MoneyManagementName))
{
moneyManagement = await _moneyManagementService.GetMoneyMangement(user, request.MoneyManagementName);
if (moneyManagement == null)
return BadRequest("Money management not found");
}
else
{
moneyManagement = request.MoneyManagement;
moneyManagement?.FormatPercentage();
}
// Update config with money management
var backtestConfig = new TradingBotConfig
{
AccountName = request.Config.AccountName,
MoneyManagement = moneyManagement,
Ticker = request.Config.Ticker,
ScenarioName = request.Config.ScenarioName,
Timeframe = request.Config.Timeframe,
IsForWatchingOnly = request.WatchOnly,
BotTradingBalance = request.Balance,
BotType = request.Config.BotType,
IsForBacktest = true,
CooldownPeriod = request.Config.CooldownPeriod,
MaxLossStreak = request.Config.MaxLossStreak,
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
FlipPosition = request.Config.FlipPosition,
Name = request.Config.Name ?? $"Backtest-{request.Config.ScenarioName}-{DateTime.UtcNow:yyyyMMdd-HHmmss}"
};
switch (request.Config.BotType)
{
case BotType.SimpleBot:
break;
case BotType.ScalpingBot:
backtestResult = await _backtester.RunScalpingBotBacktest(
account,
moneyManagement,
request.Config.Ticker,
scenario,
request.Config.Timeframe,
request.Balance,
request.StartDate,
request.EndDate,
user,
request.WatchOnly,
request.Save,
cooldownPeriod: request.Config.CooldownPeriod,
maxLossStreak: request.Config.MaxLossStreak,
maxPositionTimeHours: request.Config.MaxPositionTimeHours,
flipOnlyWhenInProfit: request.Config.FlipOnlyWhenInProfit);
break;
case BotType.FlippingBot:
backtestResult = await _backtester.RunFlippingBotBacktest(
account,
moneyManagement,
request.Config.Ticker,
scenario,
request.Config.Timeframe,
request.Balance,
request.StartDate,
request.EndDate,
user,
request.WatchOnly,
request.Save,
cooldownPeriod: request.Config.CooldownPeriod,
maxLossStreak: request.Config.MaxLossStreak,
maxPositionTimeHours: request.Config.MaxPositionTimeHours,
flipOnlyWhenInProfit: request.Config.FlipOnlyWhenInProfit);
break;
}
await NotifyBacktesingSubscriberAsync(backtestResult);
return Ok(backtestResult);
}
catch (Exception ex)
{
return StatusCode(500, $"Error running backtest: {ex.Message}");
}
await NotifyBacktesingSubscriberAsync(backtestResult);
return Ok(backtestResult);
}
/// <summary>
@@ -216,4 +235,50 @@ public class BacktestController : BaseController
await _hubContext.Clients.All.SendAsync("BacktestsSubscription", backtesting);
}
}
}
/// <summary>
/// Request model for running a backtest
/// </summary>
public class RunBacktestRequest
{
/// <summary>
/// The trading bot configuration to use for the backtest
/// </summary>
public TradingBotConfig 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 strategy to use (optional if MoneyManagement is provided)
/// </summary>
public string? MoneyManagementName { get; set; }
/// <summary>
/// The money management strategy details (optional if MoneyManagementName is provided)
/// </summary>
public MoneyManagement? MoneyManagement { get; set; }
}