Update precalculated indicators values

This commit is contained in:
2025-11-12 23:26:12 +07:00
parent a8f55c80a9
commit 3b176c290c
7 changed files with 44 additions and 79 deletions

View File

@@ -4,7 +4,6 @@ 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;
@@ -186,21 +185,11 @@ public class BacktestExecutor
_logger.LogInformation("⚡ Pre-calculating indicator values for {IndicatorCount} indicators",
config.Scenario.Indicators?.Count ?? 0);
// Convert LightScenario to Scenario for CalculateIndicatorsValuesAsync
// Convert LightScenario to Scenario for CalculateIndicatorsValues
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;
preCalculatedIndicatorValues = TradingBox.CalculateIndicatorsValues(scenario, candles);
telemetry.IndicatorPreCalculationTime = Stopwatch.GetElapsedTime(indicatorCalcStart);
_logger.LogInformation(
"✅ Successfully pre-calculated indicator values for {IndicatorCount} indicator types in {Duration:F2}ms",
@@ -264,7 +253,7 @@ public class BacktestExecutor
// Run with optimized backtest path (minimize async calls)
var backtestStepStart = Stopwatch.GetTimestamp();
await tradingBot.UpdateSignals(fixedCandles);
await tradingBot.UpdateSignals(fixedCandles, preCalculatedIndicatorValues);
await tradingBot.Run();
backtestStepTotalTime += Stopwatch.GetElapsedTime(backtestStepStart);

View File

@@ -45,13 +45,6 @@ public class TradingBotBase : ITradingBot
public Candle LastCandle { get; set; }
public DateTime? LastPositionClosingTime { get; set; }
/// <summary>
/// Pre-calculated indicator values for backtesting optimization.
/// Key is IndicatorType, Value is the calculated indicator result.
/// </summary>
public Dictionary<IndicatorType, IndicatorsResultBase> PreCalculatedIndicatorValues { get; set; }
public TradingBotBase(
ILogger<TradingBotBase> logger,
IServiceScopeFactory scopeFactory,
@@ -65,7 +58,6 @@ public class TradingBotBase : ITradingBot
Positions = new Dictionary<Guid, Position>();
WalletBalances = new Dictionary<DateTime, decimal>();
PreloadSince = CandleHelpers.GetBotPreloadSinceFromTimeframe(config.Timeframe);
PreCalculatedIndicatorValues = new Dictionary<IndicatorType, IndicatorsResultBase>();
}
public async Task Start(BotStatus previousStatus)
@@ -264,7 +256,7 @@ public class TradingBotBase : ITradingBot
}
public async Task UpdateSignals(HashSet<Candle> candles,
Dictionary<DateTime, LightSignal> preCalculatedSignals = null)
Dictionary<IndicatorType, IndicatorsResultBase> preCalculatedIndicatorValues = null)
{
// Skip indicator checking if flipping is disabled and there's an open position
// This prevents unnecessary indicator calculations when we can't act on signals anyway
@@ -285,7 +277,7 @@ public class TradingBotBase : ITradingBot
if (Config.IsForBacktest)
{
var backtestSignal = TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LoopbackPeriod,
PreCalculatedIndicatorValues);
preCalculatedIndicatorValues);
if (backtestSignal == null) return;
await AddSignal(backtestSignal);
}

View File

@@ -2,13 +2,11 @@
using Managing.Application.Abstractions.Services;
using Managing.Domain.Accounts;
using Managing.Domain.Bots;
using Managing.Domain.Candles;
using Managing.Domain.Indicators;
using Managing.Domain.Scenarios;
using Managing.Domain.Shared.Helpers;
using Managing.Domain.Statistics;
using Managing.Domain.Strategies;
using Managing.Domain.Strategies.Base;
using Managing.Domain.Synth.Models;
using Managing.Domain.Trades;
using Managing.Domain.Users;
@@ -429,44 +427,6 @@ public class TradingService : ITradingService
positionIdentifier, botConfig);
}
/// <summary>
/// Calculates indicators values for a given scenario and candles.
/// </summary>
/// <param name="scenario">The scenario containing indicators.</param>
/// <param name="candles">The candles to calculate indicators for.</param>
/// <returns>A dictionary of indicator types to their calculated values.</returns>
public async Task<Dictionary<IndicatorType, IndicatorsResultBase>> CalculateIndicatorsValuesAsync(
Scenario scenario,
HashSet<Candle> candles)
{
// Offload CPU-bound indicator calculations to thread pool
return await Task.Run(() =>
{
var indicatorsValues = new Dictionary<IndicatorType, IndicatorsResultBase>();
if (scenario?.Indicators == null || scenario.Indicators.Count == 0)
{
return indicatorsValues;
}
// Build indicators from scenario
foreach (var indicator in scenario.Indicators)
{
try
{
var buildedIndicator = ScenarioHelpers.BuildIndicator(ScenarioHelpers.BaseToLight(indicator));
indicatorsValues[indicator.Type] = buildedIndicator.GetIndicatorValues(candles);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error calculating indicator {IndicatorName}: {ErrorMessage}",
indicator.Name, ex.Message);
}
}
return indicatorsValues;
});
}
public async Task<IndicatorBase?> GetIndicatorByNameUserAsync(string name, User user)
{