Update precalculated indicators values
This commit is contained in:
@@ -9,6 +9,7 @@ using Managing.Domain.Backtests;
|
|||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Candles;
|
using Managing.Domain.Candles;
|
||||||
using Managing.Domain.Scenarios;
|
using Managing.Domain.Scenarios;
|
||||||
|
using Managing.Domain.Shared.Helpers;
|
||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Strategies;
|
using Managing.Domain.Strategies;
|
||||||
using Managing.Domain.Strategies.Base;
|
using Managing.Domain.Strategies.Base;
|
||||||
@@ -346,7 +347,7 @@ public class DataController : ControllerBase
|
|||||||
{
|
{
|
||||||
// Map ScenarioRequest to domain Scenario object
|
// Map ScenarioRequest to domain Scenario object
|
||||||
var domainScenario = MapScenarioRequestToScenario(request.Scenario);
|
var domainScenario = MapScenarioRequestToScenario(request.Scenario);
|
||||||
indicatorsValues = await _tradingService.CalculateIndicatorsValuesAsync(domainScenario, candles);
|
indicatorsValues = TradingBox.CalculateIndicatorsValues(domainScenario, candles);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(new CandlesWithIndicatorsResponse
|
return Ok(new CandlesWithIndicatorsResponse
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
using Managing.Domain.Accounts;
|
using Managing.Domain.Accounts;
|
||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Candles;
|
|
||||||
using Managing.Domain.Indicators;
|
using Managing.Domain.Indicators;
|
||||||
using Managing.Domain.Scenarios;
|
using Managing.Domain.Scenarios;
|
||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Strategies;
|
using Managing.Domain.Strategies;
|
||||||
using Managing.Domain.Strategies.Base;
|
|
||||||
using Managing.Domain.Synth.Models;
|
using Managing.Domain.Synth.Models;
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
@@ -52,15 +50,6 @@ public interface ITradingService
|
|||||||
Task<SynthRiskResult> MonitorSynthPositionRiskAsync(Ticker ticker, TradeDirection direction, decimal currentPrice,
|
Task<SynthRiskResult> MonitorSynthPositionRiskAsync(Ticker ticker, TradeDirection direction, decimal currentPrice,
|
||||||
decimal liquidationPrice, Guid positionIdentifier, TradingBotConfig botConfig);
|
decimal liquidationPrice, Guid positionIdentifier, TradingBotConfig 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>
|
|
||||||
Task<Dictionary<IndicatorType, IndicatorsResultBase>> CalculateIndicatorsValuesAsync(
|
|
||||||
Scenario scenario,
|
|
||||||
HashSet<Candle> candles);
|
|
||||||
|
|
||||||
Task<IndicatorBase?> GetIndicatorByNameUserAsync(string name, User user);
|
Task<IndicatorBase?> GetIndicatorByNameUserAsync(string name, User user);
|
||||||
Task<Scenario?> GetScenarioByNameUserAsync(string scenarioName, User user);
|
Task<Scenario?> GetScenarioByNameUserAsync(string scenarioName, User user);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using Managing.Application.Abstractions.Repositories;
|
|||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
using Managing.Application.Bots;
|
using Managing.Application.Bots;
|
||||||
using Managing.Common;
|
using Managing.Common;
|
||||||
using Managing.Core;
|
|
||||||
using Managing.Domain.Backtests;
|
using Managing.Domain.Backtests;
|
||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Candles;
|
using Managing.Domain.Candles;
|
||||||
@@ -186,21 +185,11 @@ public class BacktestExecutor
|
|||||||
_logger.LogInformation("⚡ Pre-calculating indicator values for {IndicatorCount} indicators",
|
_logger.LogInformation("⚡ Pre-calculating indicator values for {IndicatorCount} indicators",
|
||||||
config.Scenario.Indicators?.Count ?? 0);
|
config.Scenario.Indicators?.Count ?? 0);
|
||||||
|
|
||||||
// Convert LightScenario to Scenario for CalculateIndicatorsValuesAsync
|
// Convert LightScenario to Scenario for CalculateIndicatorsValues
|
||||||
var scenario = config.Scenario.ToScenario();
|
var scenario = config.Scenario.ToScenario();
|
||||||
|
|
||||||
// Calculate all indicator values once with all candles
|
// Calculate all indicator values once with all candles
|
||||||
preCalculatedIndicatorValues = await ServiceScopeHelpers
|
preCalculatedIndicatorValues = TradingBox.CalculateIndicatorsValues(scenario, candles);
|
||||||
.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;
|
|
||||||
|
|
||||||
telemetry.IndicatorPreCalculationTime = Stopwatch.GetElapsedTime(indicatorCalcStart);
|
telemetry.IndicatorPreCalculationTime = Stopwatch.GetElapsedTime(indicatorCalcStart);
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"✅ Successfully pre-calculated indicator values for {IndicatorCount} indicator types in {Duration:F2}ms",
|
"✅ 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)
|
// Run with optimized backtest path (minimize async calls)
|
||||||
var backtestStepStart = Stopwatch.GetTimestamp();
|
var backtestStepStart = Stopwatch.GetTimestamp();
|
||||||
await tradingBot.UpdateSignals(fixedCandles);
|
await tradingBot.UpdateSignals(fixedCandles, preCalculatedIndicatorValues);
|
||||||
await tradingBot.Run();
|
await tradingBot.Run();
|
||||||
|
|
||||||
backtestStepTotalTime += Stopwatch.GetElapsedTime(backtestStepStart);
|
backtestStepTotalTime += Stopwatch.GetElapsedTime(backtestStepStart);
|
||||||
|
|||||||
@@ -45,13 +45,6 @@ public class TradingBotBase : ITradingBot
|
|||||||
public Candle LastCandle { get; set; }
|
public Candle LastCandle { get; set; }
|
||||||
public DateTime? LastPositionClosingTime { 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(
|
public TradingBotBase(
|
||||||
ILogger<TradingBotBase> logger,
|
ILogger<TradingBotBase> logger,
|
||||||
IServiceScopeFactory scopeFactory,
|
IServiceScopeFactory scopeFactory,
|
||||||
@@ -65,7 +58,6 @@ public class TradingBotBase : ITradingBot
|
|||||||
Positions = new Dictionary<Guid, Position>();
|
Positions = new Dictionary<Guid, Position>();
|
||||||
WalletBalances = new Dictionary<DateTime, decimal>();
|
WalletBalances = new Dictionary<DateTime, decimal>();
|
||||||
PreloadSince = CandleHelpers.GetBotPreloadSinceFromTimeframe(config.Timeframe);
|
PreloadSince = CandleHelpers.GetBotPreloadSinceFromTimeframe(config.Timeframe);
|
||||||
PreCalculatedIndicatorValues = new Dictionary<IndicatorType, IndicatorsResultBase>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Start(BotStatus previousStatus)
|
public async Task Start(BotStatus previousStatus)
|
||||||
@@ -264,7 +256,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateSignals(HashSet<Candle> candles,
|
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
|
// 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
|
// This prevents unnecessary indicator calculations when we can't act on signals anyway
|
||||||
@@ -285,7 +277,7 @@ public class TradingBotBase : ITradingBot
|
|||||||
if (Config.IsForBacktest)
|
if (Config.IsForBacktest)
|
||||||
{
|
{
|
||||||
var backtestSignal = TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LoopbackPeriod,
|
var backtestSignal = TradingBox.GetSignal(candles, Config.Scenario, Signals, Config.Scenario.LoopbackPeriod,
|
||||||
PreCalculatedIndicatorValues);
|
preCalculatedIndicatorValues);
|
||||||
if (backtestSignal == null) return;
|
if (backtestSignal == null) return;
|
||||||
await AddSignal(backtestSignal);
|
await AddSignal(backtestSignal);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,11 @@
|
|||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
using Managing.Domain.Accounts;
|
using Managing.Domain.Accounts;
|
||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Candles;
|
|
||||||
using Managing.Domain.Indicators;
|
using Managing.Domain.Indicators;
|
||||||
using Managing.Domain.Scenarios;
|
using Managing.Domain.Scenarios;
|
||||||
using Managing.Domain.Shared.Helpers;
|
using Managing.Domain.Shared.Helpers;
|
||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Strategies;
|
using Managing.Domain.Strategies;
|
||||||
using Managing.Domain.Strategies.Base;
|
|
||||||
using Managing.Domain.Synth.Models;
|
using Managing.Domain.Synth.Models;
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
@@ -429,44 +427,6 @@ public class TradingService : ITradingService
|
|||||||
positionIdentifier, botConfig);
|
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)
|
public async Task<IndicatorBase?> GetIndicatorByNameUserAsync(string name, User user)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,10 +65,9 @@ public class RsiDivergenceIndicatorBase : IndicatorBase
|
|||||||
if (preCalculatedValues?.Rsi != null && preCalculatedValues.Rsi.Any())
|
if (preCalculatedValues?.Rsi != null && preCalculatedValues.Rsi.Any())
|
||||||
{
|
{
|
||||||
// Filter pre-calculated RSI values to match the candles we're processing
|
// Filter pre-calculated RSI values to match the candles we're processing
|
||||||
var relevantCandles = candles.TakeLast(10 * Period.Value);
|
var lastCandle = candles.Last();
|
||||||
rsiResult = preCalculatedValues.Rsi
|
rsiResult = preCalculatedValues.Rsi
|
||||||
.Where(r => relevantCandles.Any(c => c.Date == r.Date))
|
.Where(r => r.Date <= lastCandle.Date)
|
||||||
.OrderBy(r => r.Date)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -788,4 +788,39 @@ public static class TradingBox
|
|||||||
|
|
||||||
return (wins, losses);
|
return (wins, losses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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 static Dictionary<IndicatorType, IndicatorsResultBase> CalculateIndicatorsValues(
|
||||||
|
Scenario scenario,
|
||||||
|
HashSet<Candle> candles)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// Removed logging for performance in static method
|
||||||
|
// Consider adding logging back if error handling is needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return indicatorsValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user