Add cancellation token support to backtest execution and update progress handling
This commit is contained in:
@@ -125,6 +125,7 @@ public class BacktestExecutor
|
|||||||
/// <param name="bundleRequestId">Optional bundle request ID to update with backtest result</param>
|
/// <param name="bundleRequestId">Optional bundle request ID to update with backtest result</param>
|
||||||
/// <param name="metadata">Additional metadata</param>
|
/// <param name="metadata">Additional metadata</param>
|
||||||
/// <param name="progressCallback">Optional callback for progress updates (0-100)</param>
|
/// <param name="progressCallback">Optional callback for progress updates (0-100)</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token to stop execution</param>
|
||||||
/// <returns>The lightweight backtest result</returns>
|
/// <returns>The lightweight backtest result</returns>
|
||||||
public async Task<LightBacktest> ExecuteAsync(
|
public async Task<LightBacktest> ExecuteAsync(
|
||||||
TradingBotConfig config,
|
TradingBotConfig config,
|
||||||
@@ -135,7 +136,8 @@ public class BacktestExecutor
|
|||||||
string requestId = null,
|
string requestId = null,
|
||||||
Guid? bundleRequestId = null,
|
Guid? bundleRequestId = null,
|
||||||
object metadata = null,
|
object metadata = null,
|
||||||
Func<int, Task> progressCallback = null)
|
Func<int, Task> progressCallback = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (candles == null || candles.Count == 0)
|
if (candles == null || candles.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -247,6 +249,9 @@ public class BacktestExecutor
|
|||||||
// Process all candles with optimized rolling window approach
|
// Process all candles with optimized rolling window approach
|
||||||
foreach (var candle in orderedCandles)
|
foreach (var candle in orderedCandles)
|
||||||
{
|
{
|
||||||
|
// Check for cancellation (timeout or shutdown)
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
// Add to HashSet for reuse
|
// Add to HashSet for reuse
|
||||||
fixedCandles.Add(candle);
|
fixedCandles.Add(candle);
|
||||||
tradingBot.LastCandle = candle;
|
tradingBot.LastCandle = candle;
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ public class BacktestExecutorAdapter : IBacktester
|
|||||||
requestId,
|
requestId,
|
||||||
bundleRequestId: null,
|
bundleRequestId: null,
|
||||||
metadata,
|
metadata,
|
||||||
progressCallback: null);
|
progressCallback: null,
|
||||||
|
cancellationToken: default);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -88,7 +89,8 @@ public class BacktestExecutorAdapter : IBacktester
|
|||||||
requestId,
|
requestId,
|
||||||
bundleRequestId: null,
|
bundleRequestId: null,
|
||||||
metadata,
|
metadata,
|
||||||
progressCallback: null);
|
progressCallback: null,
|
||||||
|
cancellationToken: default);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1057,7 +1057,9 @@ public class TradingBotFitness : IFitness
|
|||||||
requestId: _request.RequestId,
|
requestId: _request.RequestId,
|
||||||
bundleRequestId: null, // Genetic algorithm doesn't use bundle requests
|
bundleRequestId: null, // Genetic algorithm doesn't use bundle requests
|
||||||
metadata: new GeneticBacktestMetadata(_geneticAlgorithm?.GenerationsNumber ?? 0,
|
metadata: new GeneticBacktestMetadata(_geneticAlgorithm?.GenerationsNumber ?? 0,
|
||||||
_request.RequestId)
|
_request.RequestId),
|
||||||
|
progressCallback: null,
|
||||||
|
cancellationToken: default
|
||||||
)
|
)
|
||||||
).GetAwaiter().GetResult();
|
).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,18 +267,9 @@ public class BacktestComputeWorker : BackgroundService
|
|||||||
_jobProgressTrackers.TryAdd(job.Id, progressTracker);
|
_jobProgressTrackers.TryAdd(job.Id, progressTracker);
|
||||||
|
|
||||||
// Progress callback that only updates in-memory progress (non-blocking)
|
// Progress callback that only updates in-memory progress (non-blocking)
|
||||||
|
// Timeout is now enforced via CancellationToken, not by throwing in callback
|
||||||
Func<int, Task> progressCallback = (percentage) =>
|
Func<int, Task> progressCallback = (percentage) =>
|
||||||
{
|
{
|
||||||
// Check if job has been running too long
|
|
||||||
var elapsed = DateTime.UtcNow - jobStartTime;
|
|
||||||
if (elapsed.TotalMinutes > _options.JobTimeoutMinutes)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"Job {JobId} has been running for {ElapsedMinutes} minutes, exceeding timeout of {TimeoutMinutes} minutes",
|
|
||||||
job.Id, elapsed.TotalMinutes, _options.JobTimeoutMinutes);
|
|
||||||
throw new TimeoutException($"Job exceeded timeout of {_options.JobTimeoutMinutes} minutes");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update progress in memory only - persistence happens in background
|
// Update progress in memory only - persistence happens in background
|
||||||
progressTracker.UpdateProgress(percentage);
|
progressTracker.UpdateProgress(percentage);
|
||||||
|
|
||||||
@@ -301,7 +292,8 @@ public class BacktestComputeWorker : BackgroundService
|
|||||||
requestId: job.RequestId,
|
requestId: job.RequestId,
|
||||||
bundleRequestId: job.BundleRequestId,
|
bundleRequestId: job.BundleRequestId,
|
||||||
metadata: null,
|
metadata: null,
|
||||||
progressCallback: progressCallback);
|
progressCallback: progressCallback,
|
||||||
|
cancellationToken: linkedCts.Token);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
|
catch (OperationCanceledException) when (timeoutCts.Token.IsCancellationRequested && !cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user