Improve perf for backtests
This commit is contained in:
@@ -3,13 +3,16 @@ using Managing.Application.Abstractions.Repositories;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Managing.Application.Bots;
|
||||
using Managing.Common;
|
||||
using Managing.Core;
|
||||
using Managing.Domain.Backtests;
|
||||
using Managing.Domain.Bots;
|
||||
using Managing.Domain.Candles;
|
||||
using Managing.Domain.Shared.Helpers;
|
||||
using Managing.Domain.Strategies.Base;
|
||||
using Managing.Domain.Users;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static Managing.Common.Enums;
|
||||
|
||||
namespace Managing.Application.Backtests;
|
||||
|
||||
@@ -76,7 +79,7 @@ public class BacktestExecutor
|
||||
}
|
||||
|
||||
// Create a fresh TradingBotBase instance for this backtest
|
||||
var tradingBot = await CreateTradingBotInstance(config);
|
||||
var tradingBot = CreateTradingBotInstance(config);
|
||||
tradingBot.Account = user.Accounts.First();
|
||||
|
||||
var totalCandles = candles.Count;
|
||||
@@ -86,6 +89,41 @@ public class BacktestExecutor
|
||||
_logger.LogInformation("Backtest requested by {UserId} with {TotalCandles} candles for {Ticker} on {Timeframe}",
|
||||
user.Id, totalCandles, config.Ticker, config.Timeframe);
|
||||
|
||||
// Pre-calculate indicator values once for all candles to optimize performance
|
||||
// This avoids recalculating indicators for every candle iteration
|
||||
Dictionary<IndicatorType, IndicatorsResultBase> preCalculatedIndicatorValues = null;
|
||||
if (config.Scenario != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Pre-calculating indicator values for {IndicatorCount} indicators",
|
||||
config.Scenario.Indicators?.Count ?? 0);
|
||||
|
||||
// Convert LightScenario to Scenario for CalculateIndicatorsValuesAsync
|
||||
var scenario = config.Scenario.ToScenario();
|
||||
|
||||
// Calculate all indicator values once with all candles
|
||||
preCalculatedIndicatorValues = await ServiceScopeHelpers.WithScopedService<ITradingService, Dictionary<IndicatorType, IndicatorsResultBase>>(
|
||||
_scopeFactory,
|
||||
async tradingService =>
|
||||
{
|
||||
return await tradingService.CalculateIndicatorsValuesAsync(scenario, candles);
|
||||
});
|
||||
|
||||
// Store pre-calculated values in trading bot for use during signal generation
|
||||
tradingBot.PreCalculatedIndicatorValues = preCalculatedIndicatorValues;
|
||||
|
||||
_logger.LogInformation("Successfully pre-calculated indicator values for {IndicatorCount} indicator types",
|
||||
preCalculatedIndicatorValues?.Count ?? 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to pre-calculate indicator values, will calculate on-the-fly. Error: {ErrorMessage}", ex.Message);
|
||||
// Continue with normal calculation if pre-calculation fails
|
||||
preCalculatedIndicatorValues = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize wallet balance with first candle
|
||||
tradingBot.WalletBalances.Clear();
|
||||
tradingBot.WalletBalances.Add(candles.FirstOrDefault()!.Date, config.BotTradingBalance);
|
||||
@@ -239,7 +277,7 @@ public class BacktestExecutor
|
||||
/// <summary>
|
||||
/// Creates a TradingBotBase instance for backtesting
|
||||
/// </summary>
|
||||
private async Task<TradingBotBase> CreateTradingBotInstance(TradingBotConfig config)
|
||||
private TradingBotBase CreateTradingBotInstance(TradingBotConfig config)
|
||||
{
|
||||
// Validate configuration for backtesting
|
||||
if (config == null)
|
||||
|
||||
@@ -139,41 +139,6 @@ namespace Managing.Application.Backtests
|
||||
return await RunTradingBotBacktest(config, startDate, endDate, user, false, withCandles, requestId, metadata);
|
||||
}
|
||||
|
||||
// Removed RunBacktestWithCandles - backtests now run via compute workers
|
||||
// This method is kept for backward compatibility but should not be called directly
|
||||
|
||||
private async Task<HashSet<Candle>> GetCandles(Ticker ticker, Timeframe timeframe,
|
||||
DateTime startDate, DateTime endDate)
|
||||
{
|
||||
var candles = await _exchangeService.GetCandlesInflux(TradingExchanges.Evm, ticker,
|
||||
startDate, timeframe, endDate);
|
||||
|
||||
if (candles == null || candles.Count == 0)
|
||||
throw new Exception(
|
||||
$"No candles for {ticker} on {timeframe} timeframe for start {startDate} to end {endDate}");
|
||||
|
||||
return candles;
|
||||
}
|
||||
|
||||
|
||||
// Removed CreateCleanConfigForOrleans - no longer needed with job queue approach
|
||||
|
||||
private async Task SendBacktestNotificationIfCriteriaMet(Backtest backtest)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (backtest.Score > 60)
|
||||
{
|
||||
await _messengerService.SendBacktestNotification(backtest);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to send backtest notification for backtest {Id}", backtest.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> DeleteBacktestAsync(string id)
|
||||
{
|
||||
try
|
||||
@@ -243,7 +208,6 @@ namespace Managing.Application.Backtests
|
||||
return (backtests, totalCount);
|
||||
}
|
||||
|
||||
|
||||
public async Task<Backtest> GetBacktestByIdForUserAsync(User user, string id)
|
||||
{
|
||||
var backtest = await _backtestRepository.GetBacktestByIdForUserAsync(user, id);
|
||||
@@ -605,7 +569,5 @@ namespace Managing.Application.Backtests
|
||||
if (string.IsNullOrWhiteSpace(requestId) || response == null) return;
|
||||
await _hubContext.Clients.Group($"bundle-{requestId}").SendAsync("BundleBacktestUpdate", response);
|
||||
}
|
||||
|
||||
// Removed TriggerBundleBacktestGrain methods - bundle backtests now use job queue
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user