Test strategy combo
This commit is contained in:
@@ -169,7 +169,8 @@ public class BacktestController : BaseController
|
||||
MaxPositionTimeHours = request.Config.MaxPositionTimeHours,
|
||||
FlipOnlyWhenInProfit = request.Config.FlipOnlyWhenInProfit,
|
||||
FlipPosition = request.Config.FlipPosition,
|
||||
Name = request.Config.Name ?? $"Backtest-{request.Config.ScenarioName}-{DateTime.UtcNow:yyyyMMdd-HHmmss}",
|
||||
Name = request.Config.Name ??
|
||||
$"Backtest-{request.Config.ScenarioName}-{DateTime.UtcNow:yyyyMMdd-HHmmss}",
|
||||
CloseEarlyWhenProfitable = request.Config.CloseEarlyWhenProfitable
|
||||
};
|
||||
|
||||
@@ -185,8 +186,7 @@ public class BacktestController : BaseController
|
||||
request.StartDate,
|
||||
request.EndDate,
|
||||
user,
|
||||
request.Save,
|
||||
null);
|
||||
request.Save);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,68 +8,38 @@ namespace Managing.Application.Abstractions.Services
|
||||
public interface IBacktester
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs a unified trading bot backtest with the specified configuration and date range.
|
||||
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||
/// Runs a trading bot backtest with the specified configuration and date range.
|
||||
/// Automatically handles different bot types based on config.BotType.
|
||||
/// </summary>
|
||||
/// <param name="config">The trading bot configuration</param>
|
||||
/// <param name="config">The trading bot configuration (must include Scenario object or ScenarioName)</param>
|
||||
/// <param name="startDate">The start date for the backtest</param>
|
||||
/// <param name="endDate">The end date for the backtest</param>
|
||||
/// <param name="user">The user running the backtest</param>
|
||||
/// <param name="user">The user running the backtest (optional)</param>
|
||||
/// <param name="save">Whether to save the backtest results</param>
|
||||
/// <param name="initialCandles">Optional pre-loaded candles</param>
|
||||
/// <returns>The backtest results</returns>
|
||||
Task<Backtest> RunTradingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null);
|
||||
bool save = false);
|
||||
|
||||
/// <summary>
|
||||
/// Runs a unified trading bot backtest with pre-loaded candles.
|
||||
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||
/// Runs a trading bot backtest with pre-loaded candles.
|
||||
/// Automatically handles different bot types based on config.BotType.
|
||||
/// </summary>
|
||||
/// <param name="config">The trading bot configuration</param>
|
||||
/// <param name="config">The trading bot configuration (must include Scenario object or ScenarioName)</param>
|
||||
/// <param name="candles">The candles to use for backtesting</param>
|
||||
/// <param name="user">The user running the backtest</param>
|
||||
/// <param name="user">The user running the backtest (optional)</param>
|
||||
/// <returns>The backtest results</returns>
|
||||
Task<Backtest> RunTradingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
List<Candle> candles,
|
||||
User user = null);
|
||||
|
||||
// Legacy methods - maintained for backward compatibility
|
||||
Task<Backtest> RunScalpingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null);
|
||||
|
||||
Task<Backtest> RunFlippingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null);
|
||||
|
||||
// Additional methods for backtest management
|
||||
bool DeleteBacktest(string id);
|
||||
bool DeleteBacktests();
|
||||
|
||||
Task<Backtest> RunScalpingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
List<Candle> candles,
|
||||
User user = null);
|
||||
|
||||
Task<Backtest> RunFlippingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
List<Candle> candles,
|
||||
User user = null);
|
||||
|
||||
// User-specific operations
|
||||
Task<IEnumerable<Backtest>> GetBacktestsByUser(User user);
|
||||
Backtest GetBacktestByIdForUser(User user, string id);
|
||||
bool DeleteBacktestByUser(User user, string id);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -283,13 +283,12 @@ public class StatisticService : IStatisticService
|
||||
CloseEarlyWhenProfitable = false
|
||||
};
|
||||
|
||||
var backtest = await _backtester.RunScalpingBotBacktest(
|
||||
var backtest = await _backtester.RunTradingBotBacktest(
|
||||
config,
|
||||
DateTime.Now.AddDays(-7),
|
||||
DateTime.Now,
|
||||
null,
|
||||
false,
|
||||
null);
|
||||
false);
|
||||
|
||||
return backtest.Signals;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Managing.Application.Abstractions
|
||||
decimal GetTotalFees();
|
||||
void LoadStrategies(IEnumerable<IStrategy> strategies);
|
||||
void LoadScenario(string scenarioName);
|
||||
void LoadScenario(Scenario scenario);
|
||||
void UpdateStrategiesValues();
|
||||
Task LoadAccount();
|
||||
Task<Position> OpenPositionManually(TradeDirection direction);
|
||||
|
||||
@@ -56,47 +56,32 @@ namespace Managing.Application.Backtesting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a unified trading bot backtest with the specified configuration and date range.
|
||||
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||
/// Runs a trading bot backtest with the specified configuration and date range.
|
||||
/// Automatically handles different bot types based on config.BotType.
|
||||
/// </summary>
|
||||
/// <param name="config">The trading bot configuration</param>
|
||||
/// <param name="config">The trading bot configuration (must include Scenario object or ScenarioName)</param>
|
||||
/// <param name="startDate">The start date for the backtest</param>
|
||||
/// <param name="endDate">The end date for the backtest</param>
|
||||
/// <param name="user">The user running the backtest</param>
|
||||
/// <param name="user">The user running the backtest (optional)</param>
|
||||
/// <param name="save">Whether to save the backtest results</param>
|
||||
/// <param name="initialCandles">Optional pre-loaded candles</param>
|
||||
/// <returns>The backtest results</returns>
|
||||
public async Task<Backtest> RunTradingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null)
|
||||
bool save = false)
|
||||
{
|
||||
var account = await GetAccountFromConfig(config);
|
||||
var candles = GetCandles(account, config.Ticker, config.Timeframe, startDate, endDate);
|
||||
|
||||
// Set FlipPosition based on BotType
|
||||
config.FlipPosition = config.BotType == BotType.FlippingBot;
|
||||
var result = await RunBacktestWithCandles(config, candles, user);
|
||||
|
||||
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
||||
tradingBot.LoadScenario(config.ScenarioName);
|
||||
tradingBot.User = user;
|
||||
await tradingBot.LoadAccount();
|
||||
|
||||
var candles = initialCandles ?? GetCandles(account, config.Ticker, config.Timeframe, startDate, endDate);
|
||||
var result = GetBacktestingResult(config, tradingBot, candles);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
result.User = user;
|
||||
}
|
||||
|
||||
// Set start and end dates
|
||||
result.StartDate = startDate;
|
||||
result.EndDate = endDate;
|
||||
|
||||
if (save)
|
||||
if (save && user != null)
|
||||
{
|
||||
_backtestRepository.InsertBacktestForUser(user, result);
|
||||
}
|
||||
@@ -105,25 +90,48 @@ namespace Managing.Application.Backtesting
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a unified trading bot backtest with pre-loaded candles.
|
||||
/// Automatically handles ScalpingBot and FlippingBot behavior based on config.BotType.
|
||||
/// Runs a trading bot backtest with pre-loaded candles.
|
||||
/// Automatically handles different bot types based on config.BotType.
|
||||
/// </summary>
|
||||
/// <param name="config">The trading bot configuration</param>
|
||||
/// <param name="config">The trading bot configuration (must include Scenario object or ScenarioName)</param>
|
||||
/// <param name="candles">The candles to use for backtesting</param>
|
||||
/// <param name="user">The user running the backtest</param>
|
||||
/// <param name="user">The user running the backtest (optional)</param>
|
||||
/// <returns>The backtest results</returns>
|
||||
public async Task<Backtest> RunTradingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
List<Candle> candles,
|
||||
User user = null)
|
||||
{
|
||||
var account = await GetAccountFromConfig(config);
|
||||
|
||||
return await RunBacktestWithCandles(config, candles, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core backtesting logic - handles the actual backtest execution with pre-loaded candles
|
||||
/// </summary>
|
||||
private async Task<Backtest> RunBacktestWithCandles(
|
||||
TradingBotConfig config,
|
||||
List<Candle> candles,
|
||||
User user = null)
|
||||
{
|
||||
// Set FlipPosition based on BotType
|
||||
config.FlipPosition = config.BotType == BotType.FlippingBot;
|
||||
|
||||
var tradingBot = _botFactory.CreateBacktestTradingBot(config);
|
||||
tradingBot.LoadScenario(config.ScenarioName);
|
||||
|
||||
// Load scenario - prefer Scenario object over ScenarioName
|
||||
if (config.Scenario != null)
|
||||
{
|
||||
tradingBot.LoadScenario(config.Scenario);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(config.ScenarioName))
|
||||
{
|
||||
tradingBot.LoadScenario(config.ScenarioName);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Either Scenario object or ScenarioName must be provided in TradingBotConfig");
|
||||
}
|
||||
|
||||
tradingBot.User = user;
|
||||
await tradingBot.LoadAccount();
|
||||
|
||||
@@ -137,73 +145,25 @@ namespace Managing.Application.Backtesting
|
||||
return result;
|
||||
}
|
||||
|
||||
// Legacy methods - maintained for backward compatibility
|
||||
public async Task<Backtest> RunScalpingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null)
|
||||
{
|
||||
config.BotType = BotType.ScalpingBot; // Ensure correct type
|
||||
return await RunTradingBotBacktest(config, startDate, endDate, user, save, initialCandles);
|
||||
}
|
||||
|
||||
public async Task<Backtest> RunFlippingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
User user = null,
|
||||
bool save = false,
|
||||
List<Candle>? initialCandles = null)
|
||||
{
|
||||
config.BotType = BotType.FlippingBot; // Ensure correct type
|
||||
return await RunTradingBotBacktest(config, startDate, endDate, user, save, initialCandles);
|
||||
}
|
||||
|
||||
public async Task<Backtest> RunScalpingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
List<Candle> candles,
|
||||
User user = null)
|
||||
{
|
||||
config.BotType = BotType.ScalpingBot; // Ensure correct type
|
||||
return await RunTradingBotBacktest(config, candles, user);
|
||||
}
|
||||
|
||||
public async Task<Backtest> RunFlippingBotBacktest(
|
||||
TradingBotConfig config,
|
||||
List<Candle> candles,
|
||||
User user = null)
|
||||
{
|
||||
config.BotType = BotType.FlippingBot; // Ensure correct type
|
||||
return await RunTradingBotBacktest(config, candles, user);
|
||||
}
|
||||
|
||||
private async Task<Account> GetAccountFromConfig(TradingBotConfig config)
|
||||
{
|
||||
// Use the account service to get the actual account
|
||||
var account = await _accountService.GetAccount(config.AccountName, false, false);
|
||||
if (account != null)
|
||||
{
|
||||
return account;
|
||||
}
|
||||
|
||||
// Fallback: create a basic account structure if not found
|
||||
return new Account
|
||||
{
|
||||
Name = config.AccountName,
|
||||
Exchange = TradingExchanges.GmxV2 // Default exchange, should be configurable
|
||||
Exchange = TradingExchanges.GmxV2
|
||||
};
|
||||
}
|
||||
|
||||
private List<Candle> GetCandles(Account account, Ticker ticker, Timeframe timeframe,
|
||||
DateTime startDate, DateTime endDate)
|
||||
{
|
||||
List<Candle> candles;
|
||||
|
||||
// Use specific date range
|
||||
candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker,
|
||||
var candles = _exchangeService.GetCandlesInflux(account.Exchange, ticker,
|
||||
startDate, timeframe, endDate).Result;
|
||||
|
||||
if (candles == null || candles.Count == 0)
|
||||
@@ -326,13 +286,10 @@ namespace Managing.Application.Backtesting
|
||||
return strategiesValues;
|
||||
}
|
||||
|
||||
|
||||
public bool DeleteBacktest(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Since we no longer have a general DeleteBacktestById method in the repository,
|
||||
// this should be implemented using DeleteBacktestByIdForUser with null
|
||||
_backtestRepository.DeleteBacktestByIdForUser(null, id);
|
||||
return true;
|
||||
}
|
||||
@@ -347,8 +304,6 @@ namespace Managing.Application.Backtesting
|
||||
{
|
||||
try
|
||||
{
|
||||
// Since we no longer have a general DeleteAllBacktests method in the repository,
|
||||
// this should be implemented using DeleteAllBacktestsForUser with null
|
||||
_backtestRepository.DeleteAllBacktestsForUser(null);
|
||||
return true;
|
||||
}
|
||||
@@ -363,10 +318,8 @@ namespace Managing.Application.Backtesting
|
||||
{
|
||||
var backtests = _backtestRepository.GetBacktestsByUser(user).ToList();
|
||||
|
||||
// For each backtest, ensure candles are loaded
|
||||
foreach (var backtest in backtests)
|
||||
{
|
||||
// If the backtest has no candles or only a few sample candles, retrieve them
|
||||
if (backtest.Candles == null || backtest.Candles.Count == 0 || backtest.Candles.Count < 10)
|
||||
{
|
||||
try
|
||||
@@ -386,7 +339,6 @@ namespace Managing.Application.Backtesting
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to retrieve candles for backtest {Id}", backtest.Id);
|
||||
// Continue with the next backtest if there's an error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,22 +348,18 @@ namespace Managing.Application.Backtesting
|
||||
|
||||
public Backtest GetBacktestByIdForUser(User user, string id)
|
||||
{
|
||||
// Get the backtest from the repository
|
||||
var backtest = _backtestRepository.GetBacktestByIdForUser(user, id);
|
||||
|
||||
if (backtest == null)
|
||||
return null;
|
||||
|
||||
// If the backtest has no candles or only a few sample candles, retrieve them
|
||||
if (backtest.Candles == null || backtest.Candles.Count == 0 || backtest.Candles.Count < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the account
|
||||
var account = new Account
|
||||
{ Name = backtest.Config.AccountName, Exchange = TradingExchanges.Evm };
|
||||
|
||||
// Use the stored start and end dates to retrieve candles
|
||||
var candles = _exchangeService.GetCandlesInflux(
|
||||
account.Exchange,
|
||||
backtest.Config.Ticker,
|
||||
@@ -427,7 +375,6 @@ namespace Managing.Application.Backtesting
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to retrieve candles for backtest {Id}", id);
|
||||
// Return the backtest without candles if there's an error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -145,6 +145,20 @@ public class TradingBot : Bot, ITradingBot
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadScenario(Scenario scenario)
|
||||
{
|
||||
if (scenario == null)
|
||||
{
|
||||
Logger.LogWarning("Null scenario provided");
|
||||
Stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
Scenario = scenario;
|
||||
LoadStrategies(ScenarioHelpers.GetStrategiesFromScenario(scenario));
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadStrategies(IEnumerable<IStrategy> strategies)
|
||||
{
|
||||
foreach (var strategy in strategies)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Managing.Domain.MoneyManagements;
|
||||
using Managing.Domain.Scenarios;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Domain.Bots;
|
||||
@@ -9,7 +10,6 @@ public class TradingBotConfig
|
||||
[Required] public string AccountName { get; set; }
|
||||
[Required] public MoneyManagement MoneyManagement { get; set; }
|
||||
[Required] public Ticker Ticker { get; set; }
|
||||
[Required] public string ScenarioName { get; set; }
|
||||
[Required] public Timeframe Timeframe { get; set; }
|
||||
[Required] public bool IsForWatchingOnly { get; set; }
|
||||
[Required] public decimal BotTradingBalance { get; set; }
|
||||
@@ -20,6 +20,17 @@ public class TradingBotConfig
|
||||
[Required] public bool FlipPosition { get; set; }
|
||||
[Required] public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scenario object containing all strategies. When provided, this takes precedence over ScenarioName.
|
||||
/// This allows running backtests without requiring scenarios to be saved in the database.
|
||||
/// </summary>
|
||||
public Scenario Scenario { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The scenario name to load from database. Only used when Scenario object is not provided.
|
||||
/// </summary>
|
||||
public string ScenarioName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum time in hours that a position can remain open before being automatically closed.
|
||||
/// If null, time-based position closure is disabled.
|
||||
|
||||
Reference in New Issue
Block a user